无法使用boto3检索s3存储桶中所有文件夹的名称

问题描述 投票:0回答:1

我正在使用

list_objects_v2
获取 s3 存储桶中的文件夹列表

以下函数用于获取文件夹名称

def get_folders(source_bucket):
    pages = paginator.paginate(Bucket=source_bucket)
    for page in pages:
        for obj in page['Contents']:
            if obj['Key'].endswith('/'):
                folders.append(obj['Key'])
    return folders

并非所有文件夹都会返回。该函数仅返回以下几个文件夹:

check_multiple_extract/
dataset_cities/ 
epex_dayahead_auction_half_hourly/ 
multiple_schedule_schedule_1/
penguins_sql_server/
penguins_sql_server_bcp/
penguins_sql_server_bcp_append/
run_sql_commands/
test/

该存储桶包含以下文件夹(20 个文件夹)

有人可以协助解决这个问题吗?

python amazon-web-services amazon-s3 boto3
1个回答
3
投票

重要的是要认识到 S3 中没有目录。

S3 确实有公共前缀的概念,但这只是一个方便因素,允许您列出具有共享公共前缀的对象。 S3 的某些表示层将此视为类似于目录的概念,但它们是不同的概念。与目录不同,公共前缀只是某些对象共享的前缀,除了某些对象共享前缀的事实之外,它不存在。

值得注意的是:AWS Web Interface 允许您创建空“文件夹”。实际上,它正在创建一个带有尾部斜杠的零字节对象,以创建纯粹用于 UI 目的的公共前缀。同样,S3 中不存在目录,而只是对象,因此它必须创建一个对象来为您创建公共前缀。 这些是您当前调用返回的少数项目,包括在某个时刻手动创建的常见前缀。

要列出所有可用的公共前缀,您需要枚举存储桶中的所有对象并从中确定公共前缀列表。您可以通过完整扫描来做到这一点:

def get_common_prefixes_by_list(s3, source_bucket, base_prefix=""):
    # List all objects in a bucket, and return only the prefixes
    paginator = s3.get_paginator('list_objects_v2')
    prefixes_seen = set()
    # Call into list_object_v2 to list all objects in the bucket
    pages = paginator.paginate(Bucket=source_bucket, Prefix=base_prefix)
    for page in pages:
        for obj in page.get('Contents', []):
            # Pull out the key for each object and split it into the parts 
            # based off the delimiter
            key = obj['Key']
            key = key.split('/')
            if len(key) > 1:
                # This object has at least one part to the prefix, so recreate 
                # the prefix portion of the object
                prefix = "/".join(key[:-1])
                if prefix not in prefixes_seen:
                    # And we've not previously seen this prefix, so return it
                    prefixes_seen.add(prefix)
                    yield prefix + "/"

执行此操作需要列出存储桶中的所有对象。对于相当小的存储桶和/或具有深层嵌套结构的存储桶,这将足够有效。

但是,如果您有一个非常大的存储桶,由相对较少数量的公共前缀组成,那么这样做将需要相当长的时间,因为 S3 API 每次调用中限制为 1000 个项目,并且您可能会花费大部分工作来枚举大量共享前缀的对象。在这种情况下,利用 API 的便捷方法来枚举结构是有利的:

def get_common_prefixes_by_scan(s3, source_bucket, base_prefix=""):
    # List only the common prefixes in a bucket, enumerating
    # through all of the possibilities till there are no more
    paginator = s3.get_paginator('list_objects_v2')

    # Keep a list of new prefixes we've seen to check in 
    # future passes
    prefixes_to_check = [base_prefix]
    while len(prefixes_to_check) > 0:
        current_prefix = prefixes_to_check.pop(0)
        # List all common prefixes under this prefix
        for page in paginator.paginate(Bucket=source_bucket, Prefix=current_prefix, Delimiter="/"):
            if len(page.get('CommonPrefixes', [])) == 0:
                # It's not officially documented, but in practice once ListObjectsV2 returns no more
                # common prefixes, it will not return anymore again, so bail out of the check here
                break
            for prefix in page.get('CommonPrefixes', []):
                prefix = prefix['Prefix']
                # Return the prefix, and add it to our list of prefixes to check
                yield prefix
                prefixes_to_check.append(prefix)

在这些情况下,速度会快得多。由于它需要对每个新前缀进行单独调用,因此对于少数对象的深度嵌套布局来说,它可能会更慢。

© www.soinside.com 2019 - 2024. All rights reserved.