Boto S3 API未返回完整的键列表

6 投票
4 回答
10713 浏览
提问于 2025-04-18 12:30

我在我的Python脚本中使用boto S3 API来慢慢地把数据从S3复制到我的本地文件系统。这个脚本在运行了几天后,突然出现了问题。

我用以下的API函数来获取“目录”中的键列表:

keys = bucket.get_all_keys(prefix=dirname)

但是这个函数(get_all_keys)并不总是返回完整的键列表,也就是说,我在AWS的网页界面或者通过aws s3 ls s3://path命令可以看到更多的键。

我在2.15和2.30版本上都复现了这个问题。

可能是boto缓存了我对S3的一些请求(因为我一直重复同样的请求)?有什么建议可以解决这个问题吗?

4 个回答

1

在boto3中使用分页。这段代码应该能给你答案:

def s3_list_files(bucket_name, prefix):
    paginator = client.get_paginator("list_objects")

    page_iterator = paginator.paginate(Bucket=bucket_name, Prefix=prefix)
    keys = []
    for page in page_iterator:
        if "Contents" in page:
            for key in page["Contents"]:
                keyString = key["Key"]
                keys.append(keyString)

    return keys if keys else []
3

你需要通过多次请求来分页获取结果。list() 方法可以自动帮你完成这个任务。如果你想要更好的控制,或者想从失败的请求中恢复,可以参考下面的例子。

这种逐步处理的方法在你处理数百万个对象时也更具扩展性。

marker = None
while True:
    keys = bucket.get_all_keys(marker=marker)
    last_key = None

    for k in keys:
        # TODO Do something with your keys!
        last_key = k.name

    if not keys.is_truncated:
        break

    marker = last_key

来自ResultSet 文档的信息显示,get_all_keys() 文档提到这个应该由 for 循环自动完成,但实际上并不是这样。 :(

5

我终于让它工作了!

原来我在S3的目录里有1013个文件,而get_all_keys这个函数由于AWS的限制,只能返回1000个文件。

解决办法很简单,只需要使用一个更高级的函数,不要加delimiter这个参数就行:

keys = list(bucket.list(prefix=dirname))
13

其实有个更简单的方法。Bucket这个对象本身就可以像一个迭代器一样工作,它知道怎么处理分页的响应。所以,如果还有更多的结果,它会在后台自动帮你获取这些结果。这样的话,你可以用下面的方式遍历你桶里的所有对象:

for key in bucket:
    # do something with your key

如果你想指定一个前缀,并获取所有以这个前缀开头的键,你可以这样做:

for key in bucket.list(prefix='foobar'):
    # do something with your key

或者,如果你真的非常想要建立一个对象的列表,就直接这样做:

keys = [k for k in bucket]

不过要注意,桶可以存放无限数量的键,所以在使用的时候要小心,因为这样会把所有的键都放到内存里。

撰写回答