使用boto3,如何在不检索文件夹的情况下检索S3存储桶中的所有文件?
考虑以下文件结构:
file_1.txt
folder_1/
file_2.txt
file_3.txt
folder_2/
folder_3/
file_4.txt
在这个例子中我只对4个文件感兴趣。
修改
手动解决方案是:
def count_files_in_folder(prefix):
total = 0
keys = s3_client.list_objects(Bucket=bucket_name, Prefix=prefix)
for key in keys['Contents']:
if key['Key'][-1:] != '/':
total += 1
return total
在这种情况下,总数将为4.
如果我刚刚做了
count = len(s3_client.list_objects(Bucket=bucket_name, Prefix=prefix))
结果将是7个对象(4个文件和3个文件夹):
file.txt
folder_1/
folder_1/file_2.txt
folder_1/file_3.txt
folder_1/folder_2/
folder_1/folder_2/folder_3/
folder_1/folder_2/folder_3/file_4.txt
我只想要:
file.txt
folder_1/file_2.txt
folder_1/file_3.txt
folder_1/folder_2/folder_3/file_4.txt
答案 0 :(得分:16)
S3是一个OBJECT STORE。它不会在目录树下存储文件/对象。 新来者总是混淆他们给出的“文件夹”选项,这实际上是对象的任意前缀。
object PREFIX
是一种检索由预定义的修复文件名(密钥)前缀结构组织的对象的方法,例如, 。
您可以想象使用不允许您创建目录的文件系统,但允许您使用斜杠“/”或反斜杠“\”作为分隔符创建文件名,并且您可以表示“级别”该文件由一个公共前缀。
因此在S3中,您可以使用以下“模拟目录”而不是目录。
folder1-folder2-folder3-myobject
folder1/folder2/folder3/myobject
folder1\folder2\folder3\myobject
正如您所看到的,无论您使用何种任意文件夹分隔符(分隔符),对象名称都可以存储在S3中。
但是,为了帮助用户将批量文件传输到S3,aws cli,s3_transfer api等工具会尝试简化步骤,并按照输入的本地文件夹结构创建对象名称。
因此,如果您确定所有S3对象都使用/
或\
作为分隔符,则可以使用S3transfer或AWSCcli等工具使用密钥名称进行简单下载。
这是使用资源迭代器的快速而脏的代码。使用s3.resource.object.filter将返回与list_objects()/ list_objects_v2()没有相同1000个键限制的迭代器。
import os
import boto3
s3 = boto3.resource('s3')
mybucket = s3.Bucket("mybucket")
# if blank prefix is given, return everything)
bucket_prefix="/some/prefix/here"
objs = mybucket.objects.filter(
Prefix = bucket_prefix)
for obj in objs:
path, filename = os.path.split(obj.key)
# boto3 s3 download_file will throw exception if folder not exists
try:
os.makedirs(path)
except FileExistsError:
pass
mybucket.download_file(obj.key, obj.key)
答案 1 :(得分:7)
S3中没有文件夹。你拥有的是四个名为的文件:
file_1.txt
folder_1/file_2.txt
folder_1/file_3.txt
folder_1/folder_2/folder_3/file_4.txt
这些是S3中对象的实际名称。如果你想要的是最终:
file_1.txt
file_2.txt
file_3.txt
file_4.txt
所有人都坐在本地文件系统的同一目录中,您需要操纵对象的名称才能去掉文件名。像这样的东西会起作用:
import os.path
full_name = 'folder_1/folder_2/folder_3/file_4.txt'
file_name = os.path.basename(full_name)
变量file_name
将包含'file_4.txt'
。
答案 2 :(得分:1)
过滤掉文件夹的一种方法是检查对象的结束字符,如果您确定没有文件以正斜杠结尾:
for object_summary in objects.all():
if object_summary.key[-1] == "/":
continue
答案 3 :(得分:0)
如其他答案所述,s3实际上没有目录树。但是,有一个方便的解决方法,可以利用s3“文件夹”通过使用分页器来实现零大小这一事实。如果存储桶中的所有文件的大小都> 0(当然,您需要调整您的区域),则此代码段将打印出所需的输出:
bucket_name = "bucketname"
s3 = boto3.client('s3', region_name='eu-central-1')
paginator = s3.get_paginator('list_objects')
[print(page['Key']) for page in paginator.paginate(Bucket=bucket_name).search("Contents[?Size > `0`][]")]
使用JMESPath完成过滤。
注意:当然,这也将排除大小为0的文件,但是通常您不需要存储空文件。
答案 4 :(得分:0)
使用 v2
还可以获得文件的大小,以便过滤键。
s3_client
.list_objects_v2(bucket: bucket_name, prefix: prefix)
.select { |e| e[:size] > 0 }
.map { |e| e[:key] }