Это затронет только небольшую часть пользователей, но я бы хотел, чтобы это было документировано для этой небольшой части. Из-за этой проблемы этот член этой маленькой горстки потратил 6 часов на устранение неполадок с работающим PHP-почтовым скриптом.
Если вы собираетесь в университет, в котором работает XAMPP с сайта www.AceITLab.com, вы должны знать, что наш профессор не сказал нам: брандмауэр AceITLab (а не брандмауэр Windows) блокирует MercuryMail в XAMPP , Вам придется использовать альтернативный почтовый клиент, груша работает на нас. Вам нужно будет отправить учетную запись Gmail с низкими настройками безопасности.
Да, я знаю, это абсолютно бесполезно для электронной почты реального мира. Однако, из того, что я видел, академические настройки и реальный мир часто имеют очень мало общего.
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 вы получите слой черного ящика над используемыми ресурсами.
Следующие работы для меня ... Объекты 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/
Прежде всего, в 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)
list
с prefix
и delimiter
. Полагаю, это должно сработать.
– Pirheas
4 March 2016 в 21:32
Большая реализация с 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
...
Ниже фрагмента кода возвращается ТОЛЬКО «подпапки» в «папке» из ведра 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
Мне потребовалось много времени, чтобы выяснить, но, наконец, вот простой способ перечислить содержимое подпапки в ведро 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))
AWS cli делает это (предположительно, не выбирая и не повторяя все ключи в ковше), когда вы запускаете aws s3 ls s3://my-bucket/
, поэтому я решил, что должен быть способ с помощью boto3.
Похоже, что они действительно используют префикс и разделитель - 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
Краткий ответ:
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}
ограничивает количество ключей, а не общих префиксов. Мы имеем дело с этим путем дальнейшего обрезания потока нашего итератора. в последней документации BOTO3 теперь рекомендуется использовать list_objects_v2 http://boto3.readthedocs.io/en/latest/reference/services/s3.html#S3.Client.list_objects_v2
У меня была та же проблема, но мне удалось разрешить ее с помощью 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
Если вы хотите получить имя каталога и имя элемента содержимого, замените строку печати следующим образом:
print "{}/{}".format(fileName[1], fileName[2])
И будет выведено следующее:
secondLevelFolder/item2
secondLevelFolder/item2
Надеюсь, это поможет
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