संक्षिप्त उत्तर :
का उपयोग करें 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)
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']]
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
। यह कई dirs और फ़ाइलें बनाता है। चूँकि पृष्ठ 1000 प्रविष्टियों तक हैं, इसलिए हम इसका एक से अधिक dirs और फ़ाइलों के लिए उपयोग करते हैं। dirs
केवल निर्देशिकाएं होती हैं (प्रत्येक में एक वस्तु होती है)। mixed
प्रत्येक dir के लिए 2 वस्तुओं के अनुपात के साथ dirs और ऑब्जेक्ट्स का मिश्रण होता है (प्लस 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
वास्तव में अनन्य है। यह देखते हुए कि ( AmazonS3 एपीआई के अनुसार ) कुंजी के बादMarker=topdir + 'mixed/0500_foo_a'
लिस्टिंग शुरू हो जाएगी । इसका कारण है ।.../mixed/0500_foo_b
__prev_str()
उपयोग करते समय Delimiter
, सूचीबद्ध करते समय mixed/
, प्रत्येक प्रतिक्रिया paginator
में 666 कुंजी और 334 सामान्य उपसर्ग होते हैं। यह भारी प्रतिक्रियाओं का निर्माण नहीं करने पर बहुत अच्छा है।
इसके विपरीत, सूचीबद्ध करते समय dirs/
, प्रत्येक प्रतिक्रिया paginator
में 1000 सामान्य उपसर्ग (और कोई कुंजी नहीं) होते हैं।
सीमा के रूप में PaginationConfig={'MaxItems': limit}
केवल कुंजी की संख्या को पार करना, सामान्य उपसर्ग नहीं। हम अपने पुनरावृत्तिकर्ता की धारा को और कम करके उससे निपटते हैं।