【问题标题】:Boto3 S3: Get files without getting foldersBoto3 S3:获取文件而不获取文件夹
【发布时间】:2023-03-28 10:16:01
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

    S3 是一个对象存储。它不在目录树下存储文件/对象。 新手总是混淆他们给出的“文件夹”选项,这实际上是对象的任意前缀。

    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)
    

    【讨论】:

      【解决方案2】:

      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'

      【讨论】:

      • 好吧,在 S3 中实际上有“文件夹”,它们表示为带有附加斜杠的键。文件夹的大小也为零,看起来像 AWS S3 Web 控制台 GUI 中的文件夹。所以我基本上想知道的是,是否可以整理出名称以斜线结尾的键。最好不要手动完成(如果可能的话)
      • 不,S3 中确实没有文件夹。如果您对存储桶执行ListObjects API 请求,您将看到的只是上面列出的四个文件名。 S3 允许您指定分隔符(默认为“/”),然后它将允许您像有文件夹一样导航对象,但 S3 中没有存储表示文件夹 folder1 的对象。没有办法告诉 S3 服务只返回基本文件名。您必须获取所有对象的列表,然后操作对象名称以仅提取基本文件名。
      • 不,先生,list_objects 将返回 7 个对象 boto3.readthedocs.io/en/latest/reference/services/…
      • @garnaat 这是一个很好的答案,但它需要某种重复的文件名处理。如果folder1/file.txtfolder2/file.txt 存在,当您将两者保存到同一个本地目录时,一个会覆盖另一个。
      • @Vingtoft 文件夹必须存在于您的操作系统文件系统中,然后您才能将文件放入该文件夹中。在 S3 中,不仅文件夹不需要存在,而且“文件夹”和文件之间也没有链接。如果您在尝试从 S3 下载时获得 7 个对象,那么您使用了某种 S3 工具,该工具使用 0 字节文件在您的 S3 存储桶中创建了那些假的“文件夹”。 S3 存储桶中的那些 0 字节对象不是必需的,根本不需要存在。
      【解决方案3】:

      如果您确定没有文件以正斜杠结尾,则过滤掉文件夹的一种方法是检查对象的结束字符:

      for object_summary in objects.all():
          if object_summary.key[-1] == "/":
              continue
      

      【讨论】:

        【解决方案4】:

        如其他答案所述,s3 实际上没有目录树。但是有一个方便的解决方法,利用 s3“文件夹”通过使用分页器的大小为零这一事实。如果存储桶中的所有文件的大小> 0,则此代码-sn-p 将打印出所需的输出(当然您需要调整您的区域):

        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 的文件,但通常您不需要存储空文件。

        【讨论】:

          【解决方案5】:

          使用v2,您还可以获得文件的大小,因此您可以过滤密钥。

          s3_client
            .list_objects_v2(bucket: bucket_name, prefix: prefix)
            .select { |e| e[:size] > 0 }
            .map { |e| e[:key] }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-07-26
            • 1970-01-01
            • 1970-01-01
            • 2014-03-20
            • 1970-01-01
            • 2013-10-25
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多