Boto3 S3:获取文件而不获取文件夹

时间:2017-03-08 14:18:40

标签: python amazon-web-services amazon-s3 boto3

使用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

5 个答案:

答案 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] }