如何使用Python Boto获取Amazon S3中仅文件夹的列表?

42

我正在使用boto、Python和Amazon S3。

如果我使用[key.name for key in list(self.bucket.list())],那么我将获得所有文件的键。

mybucket/files/pdf/abc.pdf
mybucket/files/pdf/abc2.pdf
mybucket/files/pdf/abc3.pdf
mybucket/files/pdf/abc4.pdf
mybucket/files/pdf/new/
mybucket/files/pdf/new/abc.pdf
mybucket/files/pdf/2011/

什么是最好的方式去

1. either get all folders from s3
2. or from that list just remove the file from the last and get the unique keys of folders

我正考虑这样做

set([re.sub("/[^/]*$","/",path) for path in mylist]

我认为你的解决方案是最好的,我可以直接获取到文件路径。 - Flavio
9个回答

49
建立在sethwm的答案之上: 获取顶层目录:
list(bucket.list("", "/"))

获取files的子目录:
list(bucket.list("files/", "/")

等等。


4
太好了,文档确实让我朝着那个方向去做,但是我似乎没有得到键列表。相反,我得到的是一个包含键和boto.s3.prefix.Prefix()对象的列表,我不太清楚该怎么使用它。你有什么想法吗? - brice
1
bucket.list会生成一个前缀对象列表。name属性可能是您要查找的内容。 - Evan Muehlhausen
1
重要提示:为了获取目录,prefix(第一个参数)应以分隔符结尾。 - Ciprian Tomoiagă

23

由于我不懂python或boto,所以我的回答可能不完整,但我想评论一下问题中的基本概念。

其他帖子中的其中一个作者是正确的:S3没有目录的概念。只有扁平的键/值对。许多应用程序假装某些分隔符指示目录条目。例如“ / ”或“ \ ”。有些应用甚至会放置一个虚拟文件,以便如果“目录”为空,您仍然可以在列表结果中看到它。

您并不总是必须将整个存储桶下载并在本地进行过滤。S3具有有限列表的概念,在其中指定您认为的路径分隔符(“ / ”、“ \ ”、“ | ”、“ foobar ”等),S3将向您返回虚拟结果,类似于您想要的结果。

http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html (请查看分隔符标头。)

此API将获取您的一个目录层级。因此,如果您的示例中有:

mybucket/files/pdf/abc.pdf
mybucket/files/pdf/abc2.pdf
mybucket/files/pdf/abc3.pdf
mybucket/files/pdf/abc4.pdf
mybucket/files/pdf/new/
mybucket/files/pdf/new/abc.pdf
mybucket/files/pdf/2011/

如果您传入一个前缀为“”且分隔符为“/”的列表,您将获得以下结果:

mybucket/files/
如果您传入一个包含前缀"mybucket/files/"和分隔符"/"的列表,您将获得以下结果:
mybucket/files/pdf/

如果您传递了一个带有前缀 "mybucket/files/pdf/" 和分隔符 "/" 的列表,您将得到以下结果:

mybucket/files/pdf/abc.pdf
mybucket/files/pdf/abc2.pdf
mybucket/files/pdf/abc3.pdf
mybucket/files/pdf/abc4.pdf
mybucket/files/pdf/new/
mybucket/files/pdf/2011/

如果你想要从结果集中删除PDF文件本身,那么此时你将需要自己处理。

至于如何在Python/Boto中实现这一点,我不清楚。希望有办法可以解决。


1
为什么在第二个示例中,使用前缀mybucket/files/pdf/和分隔符'/'列出了new/abc.pdf?我认为使用分隔符'/'表示它是内部对象,不应该被列出。@sethwm - xtreak
@Wordzilla,那是一个错误。感谢你发现了它。 - perpetual_check
谢谢。这对我今天的脚本很有帮助 :) - xtreak

20

正如j1m在评论中提到的那样,所提出的方法返回一个前缀对象。如果你想要一个名称/路径,可以使用变量 name 。例如:

作为注释中j1m建议的一种方法,该方法返回一个前缀对象。如果您需要名称/路径,可以使用变量 name 。例如:

import boto
import boto.s3

conn = boto.s3.connect_to_region('us-west-2')
bucket = conn.get_bucket(your_bucket)

folders = bucket.list("","/")
for folder in folders:
    print folder.name

如果您想获取所有的存储桶,可以将上述内容包装在buckets = conn.get_all_buckets中,然后使用for bucket in buckets:继续进行bucket.list...操作。例如:>>> buckets = S3Connection().get_all_buckets()
for bucket in buckets: ... for folder in bucket.list(): ... print folder.name
- cgseller

19

我发现以下的代码使用boto3可以运行:

import boto3
def list_folders(s3_client, bucket_name):
    response = s3_client.list_objects_v2(Bucket=bucket_name, Prefix='', Delimiter='/')
    for content in response.get('CommonPrefixes', []):
        yield content.get('Prefix')

s3_client = boto3.client('s3')
folder_list = list_folders(s3_client, bucket_name)
for folder in folder_list:
    print('Folder found: %s' % folder)

参考文献:


2
我可以通过将 session.client() 更改为 boto3.client() 来使其工作。 - Saurav Panda
分隔符选项帮助我仅获取根目录中的对象,并跳过创建的文件夹。 - Terminator
1
这真的回答了问题吗?它不会打印出存储桶中的所有文件夹,而只会打印前缀的第一个根级别。 - Anum Sheraz
2
只需将“Prefix =''”更改为您关心的任何前缀,它就会打印该级别的文件夹。 - Kyle Barron
这个怎么修改才能同时找到子文件夹呢? - Prithvi Boinpally

11

基本上 S3 中不存在所谓的文件夹。内部所有内容都存储为键(key),如果键名中包含斜杠字符,客户端可能会将其显示为文件夹。

有了这个想法,您首先应获取所有键,然后使用正则表达式过滤出路径中包含斜线的键。您现在的解决方案已经是一个很好的开端。


7
我看到您已经成功地建立了Boto连接。如果您只对一个目录感兴趣(就像您在示例中提供的那样),我认为您可以使用AWS已经提供的前缀和分隔符(链接)来解决问题。
Boto在其存储桶对象中使用此功能,您可以使用前缀和分隔符检索分层目录信息。bucket.list()将返回一个对象。
我尝试了几种方法,如果您选择在bucket.list()中使用delimiter=参数,则返回的对象是boto.s3.prefix.Prefix的迭代器,而不是boto.s3.key.Key的迭代器。换句话说,如果您尝试检索子目录,应该将delimiter='\'作为参数,因此您将获得一个对象的迭代器。
这两个返回的对象(前缀或键对象)都有.name属性,因此如果您想要将目录/文件信息作为字符串获取,可以按照以下方式打印:
from boto.s3.connection import S3Connection

key_id = '...'
secret_key = '...'

# Create connection
conn = S3Connection(key_id, secret_key)

# Get list of all buckets
allbuckets = conn.get_all_buckets()
for bucket_name in allbuckets:
    print(bucket_name)

# Connet to a specific bucket
bucket = conn.get_bucket('bucket_name')

# Get subdirectory info
for key in bucket.list(prefix='sub_directory/', delimiter='/'):
    print(key.name)

2
虽然这段代码片段是受欢迎的,可能会提供一些帮助,但如果它包括解释如何为什么解决问题,那将会大有改进。请记住,您正在回答未来读者的问题,而不仅仅是现在提问的人!请[编辑]您的答案以添加解释,并指出适用的限制和假设。 - Toby Speight
1
@TobySpeight,我添加了一些额外的信息。感谢您的评论。 - Erica Jh Lee

3

正如其他人所说,问题在于文件夹并不一定有一个键,因此您必须通过字符串搜索 / 字符并通过此找出文件夹。以下是一种生成递归字典以模仿文件夹结构的方法。

如果您想要获取所有文件和它们在文件夹中的URL

assets = {}
  for key in self.bucket.list(str(self.org) + '/'):
    path = key.name.split('/')

    identifier = assets
  for uri in path[1:-1]:
    try:
      identifier[uri]
    except:
      identifier[uri] = {}
    identifier = identifier[uri]

    if not key.name.endswith('/'):
      identifier[path[-1]] = key.generate_url(expires_in=0, query_auth=False)

return assets

如果您只想要空文件夹

folders = {}
  for key in self.bucket.list(str(self.org) + '/'):
    path = key.name.split('/')

    identifier = folders
  for uri in path[1:-1]:
    try:
      identifier[uri]
    except:
      identifier[uri] = {}
    identifier = identifier[uri]

    if key.name.endswith('/'):
      identifier[path[-1]] = {}

return folders

这可以在后期进行递归读取。

0

boto接口允许您列出存储桶的内容并给出条目的前缀。这样,您就可以获得正常文件系统中目录的条目:

import boto
AWS_ACCESS_KEY_ID = '...'
AWS_SECRET_ACCESS_KEY = '...'

conn = boto.connect_s3(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
bucket = conn.get_bucket()
bucket_entries = bucket.list(prefix='/path/to/your/directory')

for entry in bucket_entries:
    print entry

6
这将列出特定前缀路径下的所有目录和文件。 - RexFuzzle

-1
使用S3客户端完成的boto3完整示例
import boto3


def list_bucket_keys(bucket_name):
    s3_client = boto3.client("s3")
    """ :type : pyboto3.s3 """
    result = s3_client.list_objects(Bucket=bucket_name, Prefix="Trails/", Delimiter="/")
    return result['CommonPrefixes']


if __name__ == '__main__':
    print list_bucket_keys("my-s3-bucket-name")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接