我正在使用
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 个文件夹)
有人可以协助解决这个问题吗?
重要的是要认识到 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)
在这些情况下,速度会快得多。由于它需要对每个新前缀进行单独调用,因此对于少数对象的深度嵌套布局来说,它可能会更慢。