Truy xuất tên các thư mục con trong nhóm S3 từ boto3


85

Sử dụng boto3, tôi có thể truy cập nhóm AWS S3 của mình:

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

Bây giờ, thùng chứa thư mục first-level, ví dụ, bản thân nó chứa một số thư mục con được đặt tên bằng dấu thời gian 1456753904534. Tôi cần biết tên của các thư mục con này cho một công việc khác mà tôi đang làm và tôi tự hỏi liệu tôi có thể yêu cầu boto3 truy xuất chúng cho tôi hay không.

Vì vậy, tôi đã thử:

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

cung cấp một từ điển, có khóa 'Nội dung' cung cấp cho tôi tất cả các tệp cấp ba thay vì thư mục dấu thời gian cấp hai, trên thực tế, tôi nhận được một danh sách chứa những thứ như

{u'ETag ':' "etag" ', u'Key': first-level / 1456753904534 / part-00014 ', 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 '}

bạn có thể thấy rằng các tệp cụ thể, trong trường hợp này part-00014được truy xuất, trong khi tôi chỉ muốn lấy tên của thư mục. Về nguyên tắc, tôi có thể loại bỏ tên thư mục khỏi tất cả các đường dẫn nhưng thật xấu và tốn kém khi truy xuất mọi thứ ở cấp độ thứ ba để có được cấp độ thứ hai!

Tôi cũng đã thử một cái gì đó được báo cáo ở đây :

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

nhưng tôi không nhận được các thư mục ở mức mong muốn.

Có cách nào để giải quyết này?


Vì vậy, bạn đang nói rằng điều này không hoạt động? Bạn có thể đăng những gì sẽ xảy ra khi bạn chạy nó?
Jordon Phillips

1
@JordonPhillips Tôi đã thử những dòng đầu tiên của liên kết mà bạn gửi, mà tôi đã dán ở đây và tôi nhận được các tệp văn bản ở cấp độ đầu tiên của nhóm và không có thư mục nào.
mar tin

@mar tin Bạn đã bao giờ giải quyết vấn đề này chưa. Tôi đang đối mặt với một tình huống khó xử tương tự khi tôi cần phần tử đầu tiên trong mọi thư mục con của nhóm.
Ted Taylor of Life

1
@TedTaylorofLife Yea, không có cách nào khác ngoài việc lấy tất cả các đối tượng và chia nhỏ /để có được các thư mục con
mar tin

1
@ mar tin Cách duy nhất mà tôi đã làm là lấy đầu ra, đưa nó vào định dạng văn bản và phân tách bằng dấu phẩy bằng "/", sau đó sao chép và dán phần tử đầu tiên. Đau cái mông làm sao.
Ted Taylor of Life

Câu trả lời:


56

S3 là một bộ lưu trữ đối tượng, nó không có cấu trúc thư mục thực. Dấu "/" khá là thẩm mỹ. Một lý do mà mọi người muốn có cấu trúc thư mục, bởi vì họ có thể duy trì / cắt tỉa / thêm cây vào ứng dụng. Đối với S3, bạn coi cấu trúc đó như loại chỉ mục hoặc thẻ tìm kiếm.

Để thao tác đối tượng trong S3, bạn cần boto3.client hoặc boto3.resource, ví dụ: Để liệt kê tất cả đối tượng

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

Trên thực tế, nếu tên đối tượng s3 được lưu trữ bằng dấu phân tách '/'. Phiên bản mới hơn của list_objects (list_objects_v2) cho phép bạn giới hạn phản hồi đối với các khóa bắt đầu bằng tiền tố được chỉ định.

Để giới hạn các mục trong các mục trong các thư mục con nhất định:

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

Tài liệu

Một tùy chọn khác là sử dụng hàm os.path của python để giải nén tiền tố thư mục. Vấn đề là điều này sẽ yêu cầu liệt kê các đối tượng từ các thư mục không mong muốn.

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]

Nhắc nhở về boto3: boto3.resource là một API cấp cao tốt đẹp. Có những ưu và nhược điểm khi sử dụng boto3.client và boto3.resource. Nếu bạn phát triển thư viện chia sẻ nội bộ, sử dụng boto3.resource sẽ cung cấp cho bạn một lớp hộp đen trên các tài nguyên được sử dụng.


1
Điều này mang lại cho tôi kết quả giống như tôi nhận được với nỗ lực của tôi trong câu hỏi. Tôi đoán tôi sẽ phải giải quyết một cách khó khăn bằng cách lấy tất cả các khóa từ các đối tượng được trả về và tách chuỗi để lấy tên thư mục.
mar tin

1
@martina: a split trăn lười biếng và nhặt dữ liệu cuối cùng trong danh sách các ví dụ filename = keyname.split ( "/") [- 1]
mootmoot

1
@martin directory_name = os.path.dirname(directory/path/and/filename.txt)file_name = os.path.basename(directory/path/and/filename.txt)
jkdev

106

Đoạn mã dưới đây CHỈ trả về 'thư mục con' trong 'thư mục' từ nhóm 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')

Để biết thêm chi tiết, bạn có thể tham khảo tại https://github.com/boto/boto3/issues/134


12
Điều gì xảy ra nếu tôi muốn liệt kê nội dung của một thư mục con cụ thể?
azhar22k

1
@ azhar22k, tôi cho rằng bạn chỉ có thể chạy hàm đệ quy cho mỗi 'thư mục con'.
Serban Cezar

Điều gì sẽ xảy ra nếu có hơn 1000 tiền tố khác nhau?
Kostrahb

38

Tôi đã mất rất nhiều thời gian để tìm ra, nhưng cuối cùng đây là một cách đơn giản để liệt kê nội dung của một thư mục con trong S3 bucket bằng cách sử dụng boto3. Hy vọng nó giúp

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
điều gì sẽ xảy ra nếu thư mục của bạn chứa một số lượng lớn các đối tượng?
Pierre D

3
quan điểm của tôi là đây là một giải pháp không hiệu quả khủng khiếp. S3 được xây dựng để đối phó với các dấu phân cách tùy ý trong các phím. Ví dụ '/',. Điều đó cho phép bạn bỏ qua "thư mục" đầy các đối tượng mà không cần phải phân trang trên chúng. Và sau đó, ngay cả khi bạn nhấn mạnh vào một danh sách đầy đủ (tức là tương đương 'đệ quy' trong aws cli), thì bạn phải sử dụng bộ phân trang hoặc bạn sẽ chỉ liệt kê 1000 đối tượng đầu tiên.
Pierre D

Đây là một câu trả lời tuyệt vời. Đối với những người cần nó, tôi đã áp dụng một limitcho nó trong câu trả lời xuất phát của tôi .
Acumenus

38

Câu trả lời ngắn gọn :

  • Sử dụng Delimiter='/'. Điều này tránh thực hiện một danh sách đệ quy nhóm của bạn. Một số câu trả lời sai ở đây đề xuất thực hiện một danh sách đầy đủ và sử dụng một số thao tác chuỗi để truy xuất tên thư mục. Điều này có thể không hiệu quả khủng khiếp. Hãy nhớ rằng S3 hầu như không có giới hạn về số lượng đối tượng mà một thùng có thể chứa. Vì vậy, hãy tưởng tượng rằng, giữa bar/foo/, bạn có một nghìn tỷ đối tượng: bạn sẽ đợi rất lâu để có được ['bar/', 'foo/'].

  • Sử dụng Paginators. Vì lý do tương tự (S3 là giá trị gần đúng của kỹ sư), bạn phải liệt kê qua các trang và tránh lưu trữ tất cả danh sách trong bộ nhớ. Thay vào đó, hãy coi "trình nghe" của bạn như một trình lặp và xử lý luồng nó tạo ra.

  • Sử dụng boto3.client, không boto3.resource. Các resourcephiên bản dường như không xử lý tốt các Delimitertùy chọn. Nếu bạn có một tài nguyên, nói một bucket = boto3.resource('s3').Bucket(name), bạn có thể nhận được các khách hàng tương ứng với: bucket.meta.client.

Câu trả lời dài :

Sau đây là một trình lặp mà tôi sử dụng cho các nhóm đơn giản (không xử lý phiên bản).

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

Kiểm tra :

Sau đây là hữu ích để kiểm tra hành vi của paginatorlist_objects. Nó tạo ra một số dirs và tệp. Vì các trang có tới 1000 mục nhập, chúng tôi sử dụng nhiều mục trong số đó cho dirs và tệp. dirschỉ chứa các thư mục (mỗi thư mục có một đối tượng). mixedchứa hỗn hợp các dir và đối tượng, với tỷ lệ 2 đối tượng cho mỗi dir (tất nhiên là cộng với một đối tượng dưới dir; S3 chỉ lưu trữ các đối tượng).

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

Cấu trúc kết quả là:

./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

Với một chút tiến sĩ về mã được đưa ra ở trên s3listđể kiểm tra các phản hồi từ paginator, bạn có thể quan sát một số sự kiện thú vị:

  • Thực Markersự là độc quyền. Given Marker=topdir + 'mixed/0500_foo_a'sẽ làm cho danh sách bắt đầu sau khóa đó (theo API AmazonS3 ), tức là với .../mixed/0500_foo_b. Đó là lý do cho __prev_str().

  • Sử dụng Delimiter, khi liệt kê mixed/, mỗi phản hồi từ paginatorchứa 666 khóa và 334 tiền tố chung. Nó khá tốt trong việc không tạo ra các phản hồi lớn.

  • Ngược lại, khi liệt kê dirs/, mỗi phản hồi từ paginatorchứa 1000 tiền tố chung (và không có khóa).

  • Vượt qua một giới hạn dưới dạng PaginationConfig={'MaxItems': limit}giới hạn chỉ số lượng khóa, không phải các tiền tố chung. Chúng tôi giải quyết vấn đề đó bằng cách cắt bớt luồng của trình lặp của chúng tôi.


@Mehdi: nó thực sự không phức tạp lắm, đối với một hệ thống cung cấp quy mô và độ tin cậy khó tin như vậy. Nếu bạn từng giao dịch với hơn vài trăm TB, bạn sẽ nhận được sự đánh giá cao về những gì họ đang cung cấp. Hãy nhớ rằng, các ổ đĩa luôn có MTBF> 0 ... Hãy nghĩ về tác động của việc lưu trữ dữ liệu quy mô lớn. Tuyên bố từ chối trách nhiệm: Tôi là một người dùng AWS năng động và vui vẻ, không có kết nối nào khác, ngoại trừ tôi đã làm việc trên dữ liệu quy mô petabyte từ năm 2007 và nó từng khó hơn nhiều.
Pierre D

16

Nhận thức lớn với S3 là không có thư mục / thư mục chỉ có khóa. Các cấu trúc thư mục rõ ràng chỉ là thêm vào phía trước tên tập tin để trở thành 'Key', vì vậy để liệt kê các nội dung của myBucket's some/path/to/the/file/bạn có thể thử:

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

mà sẽ cung cấp cho bạn một cái gì đó như:

some/path/to/the/file/yo.jpg
some/path/to/the/file/meAndYou.gif
...

Đây là một câu trả lời hay, nhưng nó sẽ chỉ lấy tối đa 1000 đối tượng và không hơn. Tôi đã tạo ra một câu trả lời có thể truy xuất một số lượng lớn hơn các đối tượng.
Acumenus

vâng, @Acumenus, tôi đoán câu trả lời của bạn phức tạp hơn
CpILL

16

Tôi đã gặp vấn đề tương tự nhưng đã quản lý để giải quyết nó bằng cách sử dụng boto3.clientlist_objects_v2với BucketStartAftercác tham số.

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']

Kết quả đầu ra cho đoạn mã trên sẽ hiển thị như sau:

firstlevelFolder/secondLevelFolder/item1
firstlevelFolder/secondLevelFolder/item2

Tài liệu Boto3 list_objects_v2

Để loại bỏ chỉ tên thư mục, secondLevelFoldertôi chỉ sử dụng phương pháp 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]

Kết quả đầu ra cho đoạn mã trên sẽ hiển thị như sau:

secondLevelFolder
secondLevelFolder

Tài liệu phân tách Python ()

Nếu bạn muốn lấy tên thư mục VÀ tên mục nội dung, hãy thay thế dòng in bằng dòng sau:

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

Và phần sau sẽ được xuất:

secondLevelFolder/item2
secondLevelFolder/item2

Hi vọng điêu nay co ich


8

Những điều sau đây phù hợp với tôi ... Đối tượng S3:

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

Sử dụng:

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']] 

chúng tôi nhận được:

form1/
form2/
...

Với:

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

chúng tôi nhận được:

form1/section11/
form1/section12/

6

AWS cli thực hiện điều này (có lẽ mà không cần tìm nạp và lặp lại qua tất cả các khóa trong thùng) khi bạn chạy aws s3 ls s3://my-bucket/, vì vậy tôi nghĩ rằng phải có một cách sử dụng boto3.

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

Có vẻ như họ thực sự sử dụng Tiền tố và Dấu phân cách - Tôi đã có thể viết một hàm giúp tôi nhận được tất cả các thư mục ở cấp gốc của một thùng bằng cách sửa đổi mã đó một chút:

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

Đây là một giải pháp khả thi:

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

Sử dụng boto3.resource

Điều này được xây dựng dựa trên câu trả lời của itz-azhar để áp dụng một tùy chọn limit. Rõ ràng là nó đơn giản hơn đáng kể để sử dụng so với boto3.clientphiên bản.

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)

Sử dụng boto3.client

Điều này sử dụng list_objects_v2và xây dựng dựa trên câu trả lời của CpILL để cho phép truy xuất hơn 1000 đối tượng.

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

Trước hết, không có khái niệm thư mục thực trong S3. Bạn chắc chắn có thể có một tệp @'/folder/subfolder/myfile.txt' và không có thư mục cũng như thư mục con.

Để "mô phỏng" một thư mục trong S3, bạn phải tạo một tệp trống có '/' ở cuối tên của nó (xem Amazon S3 boto - cách tạo một thư mục? )

Đối với vấn đề của bạn, có lẽ bạn nên sử dụng phương thức get_all_keyscó 2 tham số: prefixdelimiter

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
Tôi e rằng tôi không có phương thức get_all_keys trên đối tượng bucket. Tôi đang sử dụng boto3 phiên bản 1.2.3.
mar tin

Chỉ cần kiểm tra boto 1.2a: ở đó, bucket có một phương thức listvới prefixdelimiter. Tôi cho rằng nó sẽ hoạt động.
Pirheas

1
Đối tượng Bucket được truy xuất khi tôi đăng trong câu hỏi không có các phương thức đó. Mình đang dùng boto3 1.2.6, link của bạn tham khảo phiên bản nào vậy?
mar tin


0

Tôi biết rằng boto3 là ​​chủ đề đang được thảo luận ở đây, nhưng tôi thấy rằng việc sử dụng awscli cho những thứ như thế này thường nhanh hơn và trực quan hơn - awscli giữ lại nhiều khả năng hơn mà boto3 đáng giá.

Ví dụ: nếu tôi có các đối tượng được lưu trong "thư mục con" được liên kết với một nhóm nhất định, tôi có thể liệt kê tất cả chúng với một cái gì đó như sau:

1) 'mydata' = tên nhóm

2) 'f1 / f2 / f3' = "đường dẫn" dẫn đến "tệp" hoặc đối tượng

3) 'foo2.csv, barfar.segy, gar.tar' = tất cả các đối tượng "bên trong" f3

Vì vậy, chúng ta có thể nghĩ về "đường dẫn tuyệt đối" dẫn đến các đối tượng này là: 'mydata / f1 / f2 / f3 / foo2.csv' ...

Sử dụng các lệnh awscli, chúng ta có thể dễ dàng liệt kê tất cả các đối tượng bên trong một "thư mục con" nhất định thông qua:

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


0

Sau đây là đoạn mã có thể xử lý phân trang, nếu bạn đang cố gắng tìm nạp một số lượng lớn các đối tượng thùng 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

Đối với Boto 1.13.3, nó trở nên đơn giản như vậy (nếu bạn bỏ qua tất cả các cân nhắc phân trang, đã được đề cập trong các câu trả lời khác):

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']]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.