Как печатать каталоги верхнего уровня [дубликат]

Это затронет только небольшую часть пользователей, но я бы хотел, чтобы это было документировано для этой небольшой части. Из-за этой проблемы этот член этой маленькой горстки потратил 6 часов на устранение неполадок с работающим PHP-почтовым скриптом.

Если вы собираетесь в университет, в котором работает XAMPP с сайта www.AceITLab.com, вы должны знать, что наш профессор не сказал нам: брандмауэр AceITLab (а не брандмауэр Windows) блокирует MercuryMail в XAMPP , Вам придется использовать альтернативный почтовый клиент, груша работает на нас. Вам нужно будет отправить учетную запись Gmail с низкими настройками безопасности.

Да, я знаю, это абсолютно бесполезно для электронной почты реального мира. Однако, из того, что я видел, академические настройки и реальный мир часто имеют очень мало общего.

20
задан mar tin 5 March 2016 в 10:50
поделиться

10 ответов

S3 - это хранилище объектов, у него нет реальной структуры каталогов. «/» Довольно косметический. Одна из причин, по которой люди хотят иметь структуру каталогов, потому что они могут поддерживать / обрезать / добавлять дерево в приложение. Для S3 вы относитесь к такой структуре как к индексу или поисковому тегу.

Чтобы манипулировать объектом в S3, вам нужно boto3.client или boto3.resource, например. Чтобы перечислить весь объект

import boto3 
s3 = boto3.client("s3")
all_objects = s3.list_objects(Bucket = 'my-bucket-name') 

http://boto3.readthedocs.org/en/latest/reference/services/s3.html#S3.Client.list_objects

Напоминание о boto3: boto3.resource - хороший API высокого уровня. Есть плюсы и минусы, использующие boto3.client vs boto3.resource. Если вы создаете внутреннюю общую библиотеку, то с помощью boto3.resource вы получите слой черного ящика над используемыми ресурсами.

10
ответ дан mootmoot 21 August 2018 в 01:29
поделиться
  • 1
    Это дает мне тот же результат, который я получаю с моей попыткой в ​​вопросе. Думаю, мне придется решить трудный путь, захватив все ключи из возвращаемых объектов и разделив строку, чтобы получить имя папки. – mar tin 7 March 2016 в 13:18
  • 2
    @martina: ленивый python раскол и забирает последние данные внутри списка, например. filename = keyname.split ("/") [- 1] – mootmoot 7 March 2016 в 17:36
  • 3
    @martin directory_name = os.path.dirname(directory/path/and/filename.txt) и file_name = os.path.basename(directory/path/and/filename.txt) – jkdev 3 November 2016 в 22:33

Следующие работы для меня ... Объекты S3:

s3://bucket/
    form1/
       section11/
          file111
          file112
       section12/
          file121
    form2/
       section21/
          file211
          file112
       section22/
          file221
          file222
          ...
      ...
   ...

С помощью:

from boto3.session import Session
s3client = session.client('s3')
resp = s3client.list_objects(Bucket=bucket, Prefix='', Delimiter="/")
forms = [x['Prefix'] for x in resp['CommonPrefixes']] 

получаем:

form1/
form2/
...

С :

resp = s3client.list_objects(Bucket=bucket, Prefix='form1/', Delimiter="/")
sections = [x['Prefix'] for x in resp['CommonPrefixes']] 

получаем:

form1/section11/
form1/section12/
2
ответ дан cem 21 August 2018 в 01:29
поделиться

Прежде всего, в S3 нет реальной концепции папок. У вас определенно есть файл @ '/folder/subfolder/myfile.txt', а не папка или подпапка.

Чтобы «имитировать» папку на S3, вы должны создать пустой файл с именем «/» в конце его имени ( см. Amazon S3 boto - как создать папку? )

Для вашей проблемы вы, вероятно, должны использовать метод get_all_keys с двумя параметрами: prefix и delimiter

https://github.com/boto/boto/blob/develop/boto/s3/bucket.py#L427

for key in bucket.get_all_keys(prefix='first-level/', delimiter='/'):
    print(key.name)
1
ответ дан Community 21 August 2018 в 01:29
поделиться
  • 1
    Я боюсь, у меня нет метода get_all_keys для объекта bucket. Я использую boto3 версию 1.2.3. – mar tin 4 March 2016 в 21:08
  • 2
    Просто проверил boto 1.2a: там, у ведра есть метод list с prefix и delimiter. Полагаю, это должно сработать. – Pirheas 4 March 2016 в 21:32
  • 3
    Объект Bucket, полученный при отправке в вопросе, не имеет этих методов. Я нахожусь на boto3 1.2.6, к какой версии относится ваша ссылка? – mar tin 7 March 2016 в 12:00
  • 4

Большая реализация с S3 заключается в том, что нет папок / каталогов только ключей. Явная структура папок просто добавлена ​​к имени файла, чтобы стать «ключом», поэтому, чтобы перечислить содержимое myBucket some/path/to/the/file/, вы можете попробовать:

s3 = boto3.client('s3')
for obj in s3.list_objects_v2(Bucket="myBucket", Prefix="some/path/to/the/file/")['Contents']:
    print(obj['Key'])

, который даст вам что-то например:

some/path/to/the/file/yoMumma.jpg
some/path/to/the/file/meAndYoMuma.gif
...
2
ответ дан CpILL 21 August 2018 в 01:29
поделиться

Ниже фрагмента кода возвращается ТОЛЬКО «подпапки» в «папке» из ведра s3.

import boto3
bucket = 'my-bucket'
#Make sure you provide / in the end
prefix = 'prefix-name-with-slash/'  

client = boto3.client('s3')
result = client.list_objects(Bucket=bucket, Prefix=prefix, Delimiter='/')
for o in result.get('CommonPrefixes'):
    print 'sub folder : ', o.get('Prefix')

Для получения дополнительной информации вы можете обратиться к https://github.com / Boto / boto3 / вопросы / 134

27
ответ дан Dipankar 21 August 2018 в 01:29
поделиться
  • 1
    Что делать, если я хочу перечислить содержимое определенной подпапки? – itz-azhar 30 June 2017 в 06:22

Мне потребовалось много времени, чтобы выяснить, но, наконец, вот простой способ перечислить содержимое подпапки в ведро S3 с помощью boto3. Надеюсь, что это поможет

prefix = "folderone/foldertwo/"
s3 = boto3.resource('s3')
bucket = s3.Bucket(name="bucket_name_here")
FilesNotFound = True
for obj in bucket.objects.filter(Prefix=prefix):
     print('{0}:{1}'.format(bucket.name, obj.key))
     FilesNotFound = False
if FilesNotFound:
     print("ALERT", "No file in {0}/{1}".format(bucket, prefix))
15
ответ дан itz-azhar 21 August 2018 в 01:29
поделиться

AWS cli делает это (предположительно, не выбирая и не повторяя все ключи в ковше), когда вы запускаете aws s3 ls s3://my-bucket/, поэтому я решил, что должен быть способ с помощью boto3.

https : //github.com/aws/aws-cli/blob/0fedc4c1b6a7aee13e2ed10c3ada778c702c22c3/awscli/customizations/s3/subcommands.py#L499

Похоже, что они действительно используют префикс и разделитель - I смог написать функцию, которая доставит мне все каталоги на корневом уровне ведра, немного изменив этот код:

def list_folders_in_bucket(bucket):
    paginator = boto3.client('s3').get_paginator('list_objects')
    folders = []
    iterator = paginator.paginate(Bucket=bucket, Prefix='', Delimiter='/', PaginationConfig={'PageSize': None})
    for response_data in iterator:
        prefixes = response_data.get('CommonPrefixes', [])
        for prefix in prefixes:
            prefix_name = prefix['Prefix']
            if prefix_name.endswith('/'):
                folders.append(prefix_name.rstrip('/'))
    return folders
1
ответ дан Paul Zielinski 21 August 2018 в 01:29
поделиться

Краткий ответ:

  • Используйте Delimiter='/' . Это позволяет избежать рекурсивного перечисления вашего ведра. Некоторые ответы здесь неверно предлагают сделать полный список и использовать некоторые манипуляции с строкой для извлечения имен каталогов. Это может быть ужасно неэффективно. Помните, что S3 практически не ограничивает количество объектов, которые может содержать ведро. Итак, представьте, что между bar/ и foo/ у вас есть триллион объектов: вы бы подождали очень долго, чтобы получить ['bar/', 'foo/'].
  • Используйте Paginators . По той же причине (S3 - это приближение инженера бесконечности), вы должны перечислить страницы и не хранить все данные в памяти. Вместо этого рассмотрите ваш «листер» как итератор и обработайте поток, который он производит.
  • Используйте boto3.client , а не boto3.resource . Версия resource, похоже, хорошо не поддерживает Delimiter. Если у вас есть ресурс, скажите bucket = boto3.resource('s3').Bucket(name), вы можете получить соответствующий клиент с помощью: bucket.meta.client.

Длинный ответ:

Ниже приводится итератор что я использую для простых ведер (без обработки версии).

import boto3
from collections import namedtuple
from operator import attrgetter


S3Obj = namedtuple('S3Obj', ['key', 'mtime', 'size', 'ETag'])


def s3list(bucket, path, start=None, end=None, recursive=True, list_dirs=True,
           list_objs=True, limit=None):
    """
    Iterator that lists a bucket's objects under path, (optionally) starting with
    start and ending before end.

    If recursive is False, then list only the "depth=0" items (dirs and objects).

    If recursive is True, then list recursively all objects (no dirs).

    Args:
        bucket:
            a boto3.resource('s3').Bucket().
        path:
            a directory in the bucket.
        start:
            optional: start key, inclusive (may be a relative path under path, or
            absolute in the bucket)
        end:
            optional: stop key, exclusive (may be a relative path under path, or
            absolute in the bucket)
        recursive:
            optional, default True. If True, lists only objects. If False, lists
            only depth 0 "directories" and objects.
        list_dirs:
            optional, default True. Has no effect in recursive listing. On
            non-recursive listing, if False, then directories are omitted.
        list_objs:
            optional, default True. If False, then directories are omitted.
        limit:
            optional. If specified, then lists at most this many items.

    Returns:
        an iterator of S3Obj.

    Examples:
        # set up
        >>> s3 = boto3.resource('s3')
        ... bucket = s3.Bucket(name)

        # iterate through all S3 objects under some dir
        >>> for p in s3ls(bucket, 'some/dir'):
        ...     print(p)

        # iterate through up to 20 S3 objects under some dir, starting with foo_0010
        >>> for p in s3ls(bucket, 'some/dir', limit=20, start='foo_0010'):
        ...     print(p)

        # non-recursive listing under some dir:
        >>> for p in s3ls(bucket, 'some/dir', recursive=False):
        ...     print(p)

        # non-recursive listing under some dir, listing only dirs:
        >>> for p in s3ls(bucket, 'some/dir', recursive=False, list_objs=False):
        ...     print(p)
"""
    kwargs = dict()
    if start is not None:
        if not start.startswith(path):
            start = os.path.join(path, start)
        # note: need to use a string just smaller than start, because
        # the list_object API specifies that start is excluded (the first
        # result is *after* start).
        kwargs.update(Marker=__prev_str(start))
    if end is not None:
        if not end.startswith(path):
            end = os.path.join(path, end)
    if not recursive:
        kwargs.update(Delimiter='/')
        if not path.endswith('/'):
            path += '/'
    kwargs.update(Prefix=path)
    if limit is not None:
        kwargs.update(PaginationConfig={'MaxItems': limit})

    paginator = bucket.meta.client.get_paginator('list_objects')
    for resp in paginator.paginate(Bucket=bucket.name, **kwargs):
        q = []
        if 'CommonPrefixes' in resp and list_dirs:
            q = [S3Obj(f['Prefix'], None, None, None) for f in resp['CommonPrefixes']]
        if 'Contents' in resp and list_objs:
            q += [S3Obj(f['Key'], f['LastModified'], f['Size'], f['ETag']) for f in resp['Contents']]
        # note: even with sorted lists, it is faster to sort(a+b)
        # than heapq.merge(a, b) at least up to 10K elements in each list
        q = sorted(q, key=attrgetter('key'))
        if limit is not None:
            q = q[:limit]
            limit -= len(q)
        for p in q:
            if end is not None and p.key >= end:
                return
            yield p


def __prev_str(s):
    if len(s) == 0:
        return s
    s, c = s[:-1], ord(s[-1])
    if c > 0:
        s += chr(c - 1)
    s += ''.join(['\u7FFF' for _ in range(10)])
    return s

Тест:

Для проверки поведения paginator и list_objects полезно следующее. Он создает несколько файлов и файлов. Поскольку страницы содержат до 1000 записей, мы используем несколько для файлов и файлов. dirs содержит только каталоги (каждый из которых имеет один объект). mixed содержит смесь dirs и объектов с отношением 2 объекта для каждого dir (плюс один объект под dir, конечно, S3 хранит только объекты).

import concurrent
def genkeys(top='tmp/test', n=2000):
    for k in range(n):
        if k % 100 == 0:
            print(k)
        for name in [
            os.path.join(top, 'dirs', f'{k:04d}_dir', 'foo'),
            os.path.join(top, 'mixed', f'{k:04d}_dir', 'foo'),
            os.path.join(top, 'mixed', f'{k:04d}_foo_a'),
            os.path.join(top, 'mixed', f'{k:04d}_foo_b'),
        ]:
            yield name


with concurrent.futures.ThreadPoolExecutor(max_workers=32) as executor:
    executor.map(lambda name: bucket.put_object(Key=name, Body='hi\n'.encode()), genkeys())

Полученная структура :

./dirs/0000_dir/foo
./dirs/0001_dir/foo
./dirs/0002_dir/foo
...
./dirs/1999_dir/foo
./mixed/0000_dir/foo
./mixed/0000_foo_a
./mixed/0000_foo_b
./mixed/0001_dir/foo
./mixed/0001_foo_a
./mixed/0001_foo_b
./mixed/0002_dir/foo
./mixed/0002_foo_a
./mixed/0002_foo_b
...
./mixed/1999_dir/foo
./mixed/1999_foo_a
./mixed/1999_foo_b

С небольшим количеством обработки кода, приведенного выше для s3list для проверки ответов от paginator, вы можете наблюдать некоторые интересные факты:

  • Marker действительно эксклюзивный. Учитывая Marker=topdir + 'mixed/0500_foo_a', этот список будет запускаться после после (согласно AmazonS3 API ), т. Е. С .../mixed/0500_foo_b. Это причина для __prev_str().
  • Используя Delimiter, при перечислении mixed/ каждый ответ от paginator содержит 666 ключей и 334 общих префиксов.
  • В отличие от этого, при перечислении dirs/ каждый ответ от paginator содержит 1000 общих префиксов (и никаких ключей).
  • Передача предел в форме PaginationConfig={'MaxItems': limit} ограничивает количество ключей, а не общих префиксов. Мы имеем дело с этим путем дальнейшего обрезания потока нашего итератора.
1
ответ дан Pierre D 21 August 2018 в 01:29
поделиться

в последней документации BOTO3 теперь рекомендуется использовать list_objects_v2 http://boto3.readthedocs.io/en/latest/reference/services/s3.html#S3.Client.list_objects_v2

7
ответ дан Sameer Girolkar 21 August 2018 в 01:29
поделиться

У меня была та же проблема, но мне удалось разрешить ее с помощью boto3.client и list_objects_v2 с параметрами Bucket и StartAfter.

s3client = boto3.client('s3')
bucket = 'my-bucket-name'
startAfter = 'firstlevelFolder/secondLevelFolder'

theobjects = s3client.list_objects_v2(Bucket=bucket, StartAfter=startAfter )
for object in theobjects['Contents']:
    print object['Key']

Результат вывода для приведенного выше кода отобразится следующее:

firstlevelFolder/secondLevelFolder/item1
firstlevelFolder/secondLevelFolder/item2

Boto3 list_objects_v2 Документация

Чтобы вырезать только имя каталога для secondLevelFolder, я просто использовал метод python split():

s3client = boto3.client('s3')
bucket = 'my-bucket-name'
startAfter = 'firstlevelFolder/secondLevelFolder'

theobjects = s3client.list_objects_v2(Bucket=bucket, StartAfter=startAfter )
for object in theobjects['Contents']:
    direcoryName = object['Key']..encode("string_escape").split('/')
    print direcoryName[1]

Результат вывода для приведенного выше кода отобразит следующее:

secondLevelFolder
secondLevelFolder

Python split () Документация

Если вы хотите получить имя каталога и имя элемента содержимого, замените строку печати следующим образом:

print "{}/{}".format(fileName[1], fileName[2])

И будет выведено следующее:

secondLevelFolder/item2
secondLevelFolder/item2

Надеюсь, это поможет

6
ответ дан Sophie Muspratt 21 August 2018 в 01:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: