B3 से S3 बाल्टी में सबफ़ोल्डर्स नामों को पुनः प्राप्त करना


85

Boto3 का उपयोग करके, मैं अपने AWS S3 बाल्टी का उपयोग कर सकता हूं:

s3 = boto3.resource('s3')
bucket = s3.Bucket('my-bucket-name')

अब, बाल्टी में फ़ोल्डर होता है first-level, जिसमें स्वयं कई टाइमस्टैम्प नाम के उप-फ़ोल्डर होते हैं, उदाहरण के लिए 1456753904534। मुझे एक और काम के लिए इन उप-फ़ोल्डरों का नाम जानने की आवश्यकता है जो मैं कर रहा हूं और मुझे आश्चर्य है कि क्या मैं boto3 को मेरे लिए पुनः प्राप्त कर सकता था।

इसलिए मैंने कोशिश की:

objs = bucket.meta.client.list_objects(Bucket='my-bucket-name')

जो एक शब्दकोश देता है, जिसकी कुंजी 'सामग्री' मुझे दूसरे स्तर के टाइमस्टैम्प निर्देशिकाओं के बजाय सभी तीसरे स्तर की फाइलें देती है, वास्तव में मुझे एक सूची मिलती है जिसमें चीजें शामिल हैं

{u'ETag ':' "etag", u'Key ': प्रथम-स्तर / १४५६3५३ ९ ०४५३४ / भाग -२०१०', u'LastModified ': datetime.datetime (2016, 2, 29, 13, 52, 24, tzinfo) = tzutc ()),
u'Owner ': {u'DisplayName': 'owner', u'ID ':' id '},
u'Size': size, u'StorageClass ':' Storageclass '}

आप देख सकते हैं कि विशिष्ट फाइलें, इस मामले part-00014में पुनर्प्राप्त की गई हैं, जबकि मैं अकेले निर्देशिका का नाम प्राप्त करना चाहता हूं। सिद्धांत रूप में मैं सभी रास्तों से निर्देशिका का नाम निकाल सकता था, लेकिन दूसरे स्तर पर आने के लिए तीसरे स्तर पर सब कुछ प्राप्त करना बदसूरत और महंगा है!

मैंने भी यहाँ रिपोर्ट की गई कुछ कोशिश की :

for o in bucket.objects.filter(Delimiter='/'):
    print(o.key)

लेकिन मुझे वांछित स्तर पर फ़ोल्डर्स नहीं मिलते हैं।

क्या इसे हल करने का कोई तरीका है?


तो आप कह रहे हैं कि यह काम नहीं करता है? क्या आप पोस्ट कर सकते हैं जब आप इसे चलाते हैं?
जार्डन फिलिप्स

1
@JordonPhillips मैंने आपके द्वारा भेजे गए उस लिंक की पहली पंक्तियों को आज़माया है, जिसे मैंने यहाँ चिपकाया है, और मुझे टेक्स्ट फ़ाइलों को बाल्टी के पहले स्तर पर और कोई फ़ोल्डर नहीं मिला है।
मार्च टिन

@मर टिन क्या आपने कभी इस मुद्दे को हल किया। मैं इसी तरह की दुविधा का सामना कर रहा हूं, जहां मुझे हर बाल्टी सबफ़ोल्डर में पहला तत्व चाहिए।
जीवन

1
@TedTaylorofLife हाँ, सब वस्तुओं /को प्राप्त करने और सबफ़ोल्डरों को विभाजित करने के अलावा कोई अन्य तरीका नहीं है
मार्च टिन

1
@ मार्च टिन जो मैंने किया है उसका एकमात्र तरीका आउटपुट लिया जाता है, इसे टेक्स्ट फॉर्मेट में फेंक दिया जाता है और "/" द्वारा कॉमा सीमांकित किया जाता है और फिर पहले तत्व को कॉपी और पेस्ट किया जाता है। कितना अधिक कष्टप्रद है।
टेड टेलर ऑफ़ लाइफ

जवाबों:


56

S3 एक ऑब्जेक्ट स्टोरेज है, इसमें वास्तविक निर्देशिका संरचना नहीं है। "/" बल्कि कॉस्मेटिक है। एक कारण है कि लोग एक निर्देशिका संरचना करना चाहते हैं, क्योंकि वे आवेदन में पेड़ को बनाए / रख सकते हैं। S3 के लिए, आप ऐसी संरचना को अनुक्रमणिका या खोज टैग के रूप में मानते हैं।

S3 में ऑब्जेक्ट में हेरफेर करने के लिए, आपको boto3.client या boto3.resource की आवश्यकता होती है, जैसे सभी ऑब्जेक्ट को सूचीबद्ध करने के लिए

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

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

वास्तव में, यदि s3 ऑब्जेक्ट नाम '/' विभाजक का उपयोग करके संग्रहीत किया जाता है। List_objects (list_objects_v2) का अधिक हालिया संस्करण आपको उन कुंजियों की प्रतिक्रिया को सीमित करने की अनुमति देता है जो निर्दिष्ट उपसर्ग के साथ शुरू होती हैं।

कुछ उप-फ़ोल्डर्स के तहत आइटम को आइटम तक सीमित करने के लिए:

    import boto3 
    s3 = boto3.client("s3")
    response = s3.list_objects_v2(
            Bucket=BUCKET,
            Prefix ='DIR1/DIR2',
            MaxKeys=100 )

प्रलेखन

एक अन्य विकल्प फ़ोल्डर के उपसर्ग को निकालने के लिए अजगर os.path फ़ंक्शन का उपयोग कर रहा है। समस्या यह है कि इसके लिए अवांछित निर्देशिकाओं से वस्तुओं को सूचीबद्ध करना होगा।

import os
s3_key = 'first-level/1456753904534/part-00014'
filename = os.path.basename(s3_key) 
foldername = os.path.dirname(s3_key)

# if you are not using conventional delimiter like '#' 
s3_key = 'first-level#1456753904534#part-00014
filename = s3_key.split("#")[-1]

Boto3 के बारे में एक अनुस्मारक: boto3.resource एक अच्छा उच्च स्तरीय एपीआई है। Boto3.client बनाम boto3.resource का उपयोग करके पेशेवरों और विपक्ष हैं। यदि आप आंतरिक साझा पुस्तकालय विकसित करते हैं, तो boto3.resource का उपयोग करने से आपको उपयोग किए गए संसाधनों पर एक ब्लैकबॉक्स परत मिलेगी।


1
यह मुझे वही परिणाम देता है जो मुझे प्रश्न में मेरे प्रयास से मिलता है। मुझे लगता है कि मुझे लौटे हुए ऑब्जेक्ट से सभी कुंजियों को पकड़कर और फ़ोल्डर का नाम पाने के लिए स्ट्रिंग को विभाजित करके कठिन रास्ता हल करना होगा।
टिन टिन

1
@ मार्टिना: एक आलसी अजगर अलग हो जाता है और सूची के अंदर अंतिम डेटा उठाता है जैसे फ़ाइल नाम = keyname.split ("/") [- 1]
mootmoot

1
@ स्मार्टिन directory_name = os.path.dirname(directory/path/and/filename.txt)औरfile_name = os.path.basename(directory/path/and/filename.txt)
जेकेदेव

106

कोड का टुकड़ा एस 3 बाल्टी से 'फ़ोल्डर' में केवल 'सबफ़ोल्डर्स' देता है।

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/issues/134 का उल्लेख कर सकते हैं


12
क्या होगा यदि मैं किसी विशेष सबफ़ोल्डर की सामग्री को सूचीबद्ध करना चाहता हूं?
अज़हर 22k

1
@ azhar22k, मुझे लगता है कि आप केवल प्रत्येक 'सब फ़ोल्डर' के लिए फ़ंक्शन को पुनरावर्ती रूप से चला सकते हैं।
सर्बान सीज़र

यदि 1000 से अधिक विभिन्न उपसर्ग हैं तो क्या होगा?
कोस्ताराब

38

मुझे यह पता लगाने में बहुत समय लगा, लेकिन अंत में यहाँ b3 के उपयोग से S3 बाल्टी में एक सबफ़ोल्डर की सामग्री को सूचीबद्ध करने का एक सरल तरीका है। आशा करता हूँ की ये काम करेगा

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))

3
क्या होगा यदि आपके फ़ोल्डर में भारी संख्या में ऑब्जेक्ट हैं?
पियरे डी

3
मेरा कहना है कि यह एक बहुत ही अयोग्य उपाय है। S3 चाबियों में मनमाने विभाजकों से निपटने के लिए बनाया गया है। उदाहरण के लिए, '/'। कि आप उन वस्तुओं से भरा "फ़ोल्डरों" को छोड़ दें, जिन पर उनके पास कोई टिप्पणी नहीं है। और फिर, यहां तक ​​कि अगर आप एक पूर्ण लिस्टिंग (यानी 'आरएसआई क्लेव में बराबर पुनरावृत्ति') पर जोर देते हैं, तो आपको पेजिनेटर का उपयोग करना होगा या आप केवल पहली 1000 वस्तुओं को सूचीबद्ध करेंगे।
पियरे डी

यह एक बेहतरीन जवाब है। जिन लोगों को इसकी आवश्यकता है, मैंने अपने व्युत्पन्न उत्तरlimit में इसके लिए आवेदन किया है ।
21:39 पर एक्यूमेनस

38

संक्षिप्त उत्तर :

  • का उपयोग करें Delimiter='/'। यह आपकी बाल्टी की पुनरावर्ती सूची बनाने से बचता है। यहां कुछ उत्तर गलत तरीके से पूरी लिस्टिंग करने और निर्देशिका नामों को पुनः प्राप्त करने के लिए कुछ स्ट्रिंग हेरफेर का उपयोग करने का सुझाव देते हैं। यह बहुत ही अयोग्य हो सकता है। याद रखें कि S3 में वस्तुतः ऐसी कोई सीमा नहीं है कि एक बाल्टी में कितनी वस्तुएँ हो सकती हैं। तो, कल्पना करो कि, के बीच bar/और foo/, आप एक खरब वस्तुओं है: आप एक बहुत लंबे समय पाने के लिए इंतजार करेंगे ['bar/', 'foo/']

  • का उपयोग करें Paginators। उसी कारण से (S3 एक इन्फिनिटी का इंजीनियर है), आपको पृष्ठों के माध्यम से सूचीबद्ध करना चाहिए और मेमोरी में सभी लिस्टिंग को संग्रहीत करने से बचना चाहिए । इसके बजाय, अपने "लिस्टर" को एक पुनरावृत्त के रूप में मानें, और उस स्ट्रीम को संभालें जो इसे पैदा करता है।

  • का उपयोग करें boto3.client, नहीं boto3.resourceresourceसंस्करण अच्छी तरह से संभाल करने के लिए प्रतीत नहीं होता 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। यह कई 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}केवल कुंजी की संख्या को पार करना, सामान्य उपसर्ग नहीं। हम अपने पुनरावृत्तिकर्ता की धारा को और कम करके उससे निपटते हैं।


@ मेहेदी: यह वास्तव में बहुत जटिल नहीं है, ऐसी प्रणाली के लिए जो इस तरह के अविश्वसनीय पैमाने और विश्वसनीयता प्रदान करती है। यदि आप कभी-कभी कुछ सौ से अधिक टीबी से निपटते हैं, तो आप जो पेशकश कर रहे हैं, उसके लिए आपको सराहना मिलेगी। याद रखें, ड्राइव में हमेशा MTBF> 0 होता है ... बड़े पैमाने पर डेटा भंडारण के निहितार्थ के बारे में सोचें। अस्वीकरण: मैं एक सक्रिय और खुश AWS उपयोगकर्ता हूं, कोई अन्य कनेक्शन नहीं है, सिवाय मैंने 2007 के बाद से पेटाबेट स्केल डेटा पर काम किया है और यह बहुत कठिन हुआ करता था।
पियरे डी

16

S3 के साथ बड़ा अहसास यह है कि कोई भी फ़ोल्डर / डायरेक्टरीज़ सिर्फ चाबियाँ नहीं हैं। स्पष्ट फ़ोल्डर संरचना बस फ़ाइलनाम के लिए prepended है 'कुंजी' बनने के लिए है, इसलिए की सामग्री को सूचीबद्ध करने के लिए 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/yo.jpg
some/path/to/the/file/meAndYou.gif
...

यह एक अच्छा उत्तर है, लेकिन यह केवल 1000 वस्तुओं तक ही पुनः प्राप्त होगा और इससे अधिक नहीं। मैंने एक व्युत्पन्न उत्तर दिया है जो बड़ी संख्या में वस्तुओं को पुनः प्राप्त कर सकता है।
एक्यूमेनस

हाँ, @Acumenus मुझे लगता है कि आपका उत्तर अधिक जटिल है
CpILL

16

मेरे पास एक ही मुद्दा था, लेकिन इसका उपयोग 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उपयोग करने के लिए मैंने सिर्फ अजगर विधि का इस्तेमाल किया है 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

उम्मीद है की यह मदद करेगा


8

निम्नलिखित मेरे लिए काम करता है ... 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/

6

जब आप दौड़ते हैं aws s3 ls s3://my-bucket/तो एडब्ल्यूएस क्ली (संभवतः बिना चूमे और सभी कुंजियों के माध्यम से पुनरावृति करता है) , तो मुझे लगा कि बोटो 3 का उपयोग करने का एक तरीका होना चाहिए।

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

ऐसा लगता है कि वे वास्तव में Prefix और Delimiter का उपयोग करते हैं - मैं एक फ़ंक्शन लिखने में सक्षम था जो मुझे उस कोड को थोड़ा संशोधित करके एक बाल्टी के मूल स्तर पर सभी निर्देशिकाएं प्राप्त करेगा:

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

2

यहाँ एक संभावित समाधान है:

def download_list_s3_folder(my_bucket,my_folder):
    import boto3
    s3 = boto3.client('s3')
    response = s3.list_objects_v2(
        Bucket=my_bucket,
        Prefix=my_folder,
        MaxKeys=1000)
    return [item["Key"] for item in response['Contents']]

1

का उपयोग करते हुए boto3.resource

यह वैकल्पिक लागू करने के लिए itz-azhar द्वारा उत्तर पर बनाता है limit। यह स्पष्ट रूप से boto3.clientसंस्करण की तुलना में उपयोग करने के लिए काफी सरल है ।

import logging
from typing import List, Optional

import boto3
from boto3_type_annotations.s3 import ObjectSummary  # pip install boto3_type_annotations

log = logging.getLogger(__name__)
_S3_RESOURCE = boto3.resource("s3")

def s3_list(bucket_name: str, prefix: str, *, limit: Optional[int] = None) -> List[ObjectSummary]:
    """Return a list of S3 object summaries."""
    # Ref: https://stackoverflow.com/a/57718002/
    return list(_S3_RESOURCE.Bucket(bucket_name).objects.limit(count=limit).filter(Prefix=prefix))


if __name__ == "__main__":
    s3_list("noaa-gefs-pds", "gefs.20190828/12/pgrb2a", limit=10_000)

का उपयोग करते हुए boto3.client

यह 1000 से अधिक वस्तुओं को पुनः प्राप्त करने की अनुमति देने के लिए CpILL द्वारा उत्तर का उपयोग करता है list_objects_v2और बनाता है ।

import logging
from typing import cast, List

import boto3

log = logging.getLogger(__name__)
_S3_CLIENT = boto3.client("s3")

def s3_list(bucket_name: str, prefix: str, *, limit: int = cast(int, float("inf"))) -> List[dict]:
    """Return a list of S3 object summaries."""
    # Ref: https://stackoverflow.com/a/57718002/
    contents: List[dict] = []
    continuation_token = None
    if limit <= 0:
        return contents
    while True:
        max_keys = min(1000, limit - len(contents))
        request_kwargs = {"Bucket": bucket_name, "Prefix": prefix, "MaxKeys": max_keys}
        if continuation_token:
            log.info(  # type: ignore
                "Listing %s objects in s3://%s/%s using continuation token ending with %s with %s objects listed thus far.",
                max_keys, bucket_name, prefix, continuation_token[-6:], len(contents))  # pylint: disable=unsubscriptable-object
            response = _S3_CLIENT.list_objects_v2(**request_kwargs, ContinuationToken=continuation_token)
        else:
            log.info("Listing %s objects in s3://%s/%s with %s objects listed thus far.", max_keys, bucket_name, prefix, len(contents))
            response = _S3_CLIENT.list_objects_v2(**request_kwargs)
        assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
        contents.extend(response["Contents"])
        is_truncated = response["IsTruncated"]
        if (not is_truncated) or (len(contents) >= limit):
            break
        continuation_token = response["NextContinuationToken"]
    assert len(contents) <= limit
    log.info("Returning %s objects from s3://%s/%s.", len(contents), bucket_name, prefix)
    return contents


if __name__ == "__main__":
    s3_list("noaa-gefs-pds", "gefs.20190828/12/pgrb2a", limit=10_000)

0

सबसे पहले, S3 में कोई वास्तविक फ़ोल्डर अवधारणा नहीं है। आपके पास निश्चित रूप से एक फ़ाइल @ '/folder/subfolder/myfile.txt'और कोई फ़ोल्डर नहीं है और न ही सबफ़ोल्डर हो सकता है।

S3 में किसी फ़ोल्डर को "अनुकरण" करने के लिए, आपको उसके नाम के अंत में '/' के साथ एक खाली फ़ाइल बनानी होगी ( अमेज़ॅन S3 बोटो देखें - फ़ोल्डर कैसे बनाएं? )

आपकी समस्या के लिए, आपको संभवतः get_all_keys2 मापदंडों के साथ विधि का उपयोग करना चाहिए : 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
मुझे डर है कि मेरे पास बाल्टी ऑब्जेक्ट पर get_all_keys का तरीका नहीं है। मैं boto3 संस्करण 1.2.3 का उपयोग कर रहा हूं।
मार्च टिन

बस चेक किया गया बोटो 1.2 ए: वहाँ, बाल्टी के listसाथ एक विधि है prefixऔर delimiter। मुझे लगता है कि यह काम करना चाहिए।
पीरहेस

1
बकेट ऑब्जेक्ट को पुनर्प्राप्त किया जाता है क्योंकि मैं प्रश्न में पोस्ट करता हूं उन विधियों में नहीं है। मैं boto3 1.2.6 पर हूं, आपका लिंक किस संस्करण का संदर्भ देता है?
टिन टिन


0

मुझे पता है कि boto3 यहाँ चर्चा की जा रही विषय है, लेकिन मुझे लगता है कि यह आमतौर पर जल्दी और अधिक सहज है कि बस कुछ इस तरह के लिए awscli का उपयोग करने के लिए सहज है - awscli अधिक क्षमताओं को बनाए रखता है जो boto3 की तुलना में इसके लायक है।

उदाहरण के लिए, यदि मेरे पास किसी दिए गए बाल्टी से जुड़े "सबफ़ोल्डर्स" में सहेजी गई वस्तुएं हैं, तो मैं उन सभी को इस तरह से कुछ के साथ सूचीबद्ध कर सकता हूं:

1) 'मायदाता' = बाल्टी नाम

2) 'f1 / f2 / f3' = "पथ" "फाइलों" या वस्तुओं के लिए अग्रणी

3) 'foo2.csv, barfar.segy, gar.tar' = सभी ऑब्जेक्ट्स "f3" के अंदर

तो, हम "पूर्ण पथ" के बारे में सोच सकते हैं जो इन वस्तुओं के लिए अग्रणी है: 'mydata / f1 / f2 / f3 / foo2.csv' ...

Awscli कमांड का उपयोग करके, हम आसानी से दिए गए "सबफ़ोल्डर" के अंदर सभी वस्तुओं को सूचीबद्ध कर सकते हैं:

aws s3 ss s3: // mydata / f1 / f2 / f3 / --recursive


0

निम्नलिखित कोड का टुकड़ा है जो पृष्ठांकन को संभाल सकता है, यदि आप बड़ी संख्या में S3 बाल्टी ऑब्जेक्ट लाने का प्रयास कर रहे हैं:

def get_matching_s3_objects(bucket, prefix="", suffix=""):

    s3 = boto3.client("s3")
    paginator = s3.get_paginator("list_objects_v2")

    kwargs = {'Bucket': bucket}

    # We can pass the prefix directly to the S3 API.  If the user has passed
    # a tuple or list of prefixes, we go through them one by one.
    if isinstance(prefix, str):
        prefixes = (prefix, )
    else:
        prefixes = prefix

    for key_prefix in prefixes:
        kwargs["Prefix"] = key_prefix

        for page in paginator.paginate(**kwargs):
            try:
                contents = page["Contents"]
            except KeyError:
                return

            for obj in contents:
                key = obj["Key"]
                if key.endswith(suffix):
                    yield obj

0

1.13.3 बोटो के लिए, यह उतना ही सरल हो जाता है जितना कि (यदि आप सभी पृष्ठांकन विचारों को छोड़ देते हैं, जो अन्य उत्तरों में शामिल थे):

def get_sub_paths(bucket, prefix):
s3 = boto3.client('s3')
response = s3.list_objects_v2(
    Bucket=bucket,
    Prefix=prefix,
    MaxKeys=1000)
return [item["Prefix"] for item in response['CommonPrefixes']]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.