如何使用Google Cloud API获取给定存储桶中的文件夹列表

Sha*_*lam 5 python google-cloud-storage google-api-python-client

我想使用Google Cloud Storage API将所有文件夹放入给定的Google Cloud存储桶或文件夹中.

例如,如果gs://abc/xyz包含三个文件夹gs://abc/xyz/x1,gs://abc/xyz/x2gs://abc/xyz/x3.API应该返回所有三个文件夹gs://abc/xyz.

它可以很容易地使用 gsutil

gsutil ls gs://abc/xyz

但我需要使用python和Google Cloud Storage API来实现.

Ant*_*eld 15

这个问题是关于上市文件夹桶/文件夹内。没有任何建议对我有用,在试用google.cloud.storageSDK 后,我怀​​疑不可能(截至 2019 年 11 月)列出存储桶中任何路径的子目录。REST API 是可能的,所以我写了这个小包装器......

from google.api_core import page_iterator
from google.cloud import storage

def _item_to_value(iterator, item):
    return item

def list_directories(bucket_name, prefix):
    if prefix and not prefix.endswith('/'):
        prefix += '/'

    extra_params = {
        "projection": "noAcl",
        "prefix": prefix,
        "delimiter": '/'
    }

    gcs = storage.Client()

    path = "/b/" + bucket_name + "/o"

    iterator = page_iterator.HTTPIterator(
        client=gcs,
        api_request=gcs._connection.api_request,
        path=path,
        items_key='prefixes',
        item_to_value=_item_to_value,
        extra_params=extra_params,
    )

    return [x for x in iterator]
Run Code Online (Sandbox Code Playgroud)

例如,如果您my-bucket包含:

  • 狗吠
    • 数据集
      • v1
      • v2

然后调用list_directories('my-bucket', 'dog-bark/datasets')将返回:

['dog-bark/datasets/v1', 'dog-bark/datasets/v2']


jte*_*ace 11

您可以使用 Python GCS API 客户端库。有关文档和下载的相关链接,请参阅Google Cloud Storage文档页面的示例和库

就您而言,首先我想指出您混淆了“存储桶”一词。我建议阅读文档的关键术语页面。你在谈论的是对象名称前缀。

您可以从GitHub 上的list-objects.py示例开始。查看列表参考页面,您需要通过bucket=abc,prefix=xyz/delimiter=/

  • 好吧,当我们使用前缀和分隔符调用 `objects().list()` 时,我们会得到匹配对象和匹配前缀的列表。正如@jterrace 回答的那样,如果我们通过 `prefix=abc/xyz` 和 `delimiter=/`,我们会得到名称以 `abc/xyz` 开头的所有对象以及可以在逻辑上被视为子文件夹的 `prefixes`。 (3认同)
  • @Robino,你是对的 - 我搞砸了。更新了答案。 (2认同)

Eka*_*ong 9

这是此答案线程的更新:

from google.cloud import storage

# Instantiates a client
storage_client = storage.Client()

# Get GCS bucket
bucket = storage_client.get_bucket(bucket_name)

# Get blobs in bucket (including all subdirectories)
blobs_all = list(bucket.list_blobs())

# Get blobs in specific subirectory
blobs_specific = list(bucket.list_blobs(prefix='path/to/subfolder/'))
Run Code Online (Sandbox Code Playgroud)

  • 虽然这可能适用于列出对象,但这个问题是关于列出子文件夹的,而这并不能做到这一点。@AntPhitlok 的答案是正确的。 (3认同)

Rob*_*ino 8

1. 访问您的客户端对象。

代码在哪里运行?

我在 Google Cloud Platform (GCP) 内部(某处)

如果您从 GCP 内部访问 Google Cloud Storage (GCS),例如 Google Kubernetes Engine (GKE),则应使用工作负载身份将 GKE 服务帐户配置为充当 GCS 服务帐户。https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity

一旦你这样做了,创建你的客户就像

import google.cloud.storage as gcs
client = gcs.Client()
Run Code Online (Sandbox Code Playgroud)

在野外

如果您在其他地方:AWS、Azure、您的开发计算机或 GCP 之外的其他地方,那么您需要选择是创建您下载的服务帐户密钥(它是一个包含加密私钥的 json 文件),还是使用工作负载身份联合,例如AWS、Azure和“朋友”提供的。

假设您已决定将新的 GCS 服务帐户文件下载到/secure/gcs.json.

PROJECT_NAME = "MY-GCP-PROJECT"
from google.oauth2.service_account import Credentials
import google.cloud.storage as gcs
client = gcs.Client(
    project=PROJECT_NAME,
    credentials=Credentials.from_service_account_file("/secure/gcs.json"),
)
Run Code Online (Sandbox Code Playgroud)

2. 向 GCS 发出列表文件夹请求

在OP中,我们试图获取xyzbucket中path内的文件夹abc。请注意,与 Linux 不同,GCS 中的路径不以 开头/,但应以 1 结尾。因此,我们将寻找前缀为 的文件夹xyz/。这只是文件夹,而不是文件夹及其所有子文件夹。

BUCKET_NAME = "abc"
blobs = client.list_blobs(
    BUCKET_NAME,
    prefix="xyz/",  # <- you need the trailing slash
    delimiter="/",
    max_results=1,
)
Run Code Online (Sandbox Code Playgroud)

请注意我们如何要求不超过一个斑点。这不是一个错误:blob 就是文件本身 - 我们只对文件夹感兴趣。设置max_results为零不起作用,请参见下文。

3. 强制延迟加载...错误..加载!

这里的几个答案已经循环了 iterator 中的每个元素blobs,这可能有数百万个元素,但我们不需要这样做。也就是说,如果我们不循环任何元素,blobs就根本不会向 GCS 发出 api 请求。

next(blobs, ...) # Force blobs to load.
print(blobs.prefixes)
Run Code Online (Sandbox Code Playgroud)

blobs变量是一个最多包含一个元素的迭代器,但是,如果您的文件夹中没有文件(在其级别),则可能有零个元素。如果有零个元素,则将next(blobs)引发StopIteration

第二个参数,省略号 ...,只是我选择的默认返回值(如果没有next元素的话)。我觉得这比,比如说,更具可读性None,因为它向读者暗示这里正在发生一些值得注意的事情。毕竟,请求一个值只是在同一行上丢弃它的代码确实具有潜在错误的所有特征,因此最好让我们的读者放心,这是故意的。

xyz最后,假设我们在aaabbb、下有一个树结构ccc,然后在下ccc有子子文件夹zzz。然后输出将是

{'xyz/aaa', 'xyz/bbb', 'xyz/ccc'}
Run Code Online (Sandbox Code Playgroud)

请注意,根据 OP 的要求,我们看不到 subsubfolder xyz/ccc/zzz


net*_*ink 6

我还需要简单列出一个桶的内容.理想情况下,我想要类似于tf.gfile提供的内容.tf.gfile支持确定条目是文件还是目录.

我尝试了以上@jterrace提供的各种链接,但我的结果不是最佳的.随着它表示其值得展示的结果.

给定具有"目录"和"文件"混合的桶,难以导航"文件系统"以找到感兴趣的项目.我在代码中提供了一些关于上面引用的代码如何工作的注释.

在任何一种情况下,我都使用带有笔记本所包含凭据的datalab笔记本.鉴于结果,我将需要使用字符串解析来确定哪些文件位于特定目录中.如果有人知道如何扩展这些方法或解析类似于tf.gfile的目录的替代方法,请回复.

方法一

import sys
import json
import argparse
import googleapiclient.discovery

BUCKET = 'bucket-sounds' 

def create_service():
    return googleapiclient.discovery.build('storage', 'v1')


def list_bucket(bucket):
    """Returns a list of metadata of the objects within the given bucket."""
    service = create_service()

    # Create a request to objects.list to retrieve a list of objects.
    fields_to_return = 'nextPageToken,items(name,size,contentType,metadata(my-key))'
    #req = service.objects().list(bucket=bucket, fields=fields_to_return)  # returns everything
    #req = service.objects().list(bucket=bucket, fields=fields_to_return, prefix='UrbanSound')  # returns everything. UrbanSound is top dir in bucket
    #req = service.objects().list(bucket=bucket, fields=fields_to_return, prefix='UrbanSound/FREE') # returns the file FREESOUNDCREDITS.TXT
    #req = service.objects().list(bucket=bucket, fields=fields_to_return, prefix='UrbanSound/FREESOUNDCREDITS.txt', delimiter='/') # same as above
    #req = service.objects().list(bucket=bucket, fields=fields_to_return, prefix='UrbanSound/data/dog_bark', delimiter='/') # returns nothing
    req = service.objects().list(bucket=bucket, fields=fields_to_return, prefix='UrbanSound/data/dog_bark/', delimiter='/') # returns files in dog_bark dir

    all_objects = []
    # If you have too many items to list in one request, list_next() will
    # automatically handle paging with the pageToken.
    while req:
        resp = req.execute()
        all_objects.extend(resp.get('items', []))
        req = service.objects().list_next(req, resp)
    return all_objects

# usage
print(json.dumps(list_bucket(BUCKET), indent=2))
Run Code Online (Sandbox Code Playgroud)

这会生成如下结果:

[
  {
    "contentType": "text/csv", 
    "name": "UrbanSound/data/dog_bark/100032.csv", 
    "size": "29"
  }, 
  {
    "contentType": "application/json", 
    "name": "UrbanSound/data/dog_bark/100032.json", 
    "size": "1858"
  } stuff snipped]
Run Code Online (Sandbox Code Playgroud)

方法二

import re
import sys
from google.cloud import storage

BUCKET = 'bucket-sounds'

# Create a Cloud Storage client.
gcs = storage.Client()

# Get the bucket that the file will be uploaded to.
bucket = gcs.get_bucket(BUCKET)

def my_list_bucket(bucket_name, limit=sys.maxsize):
  a_bucket = gcs.lookup_bucket(bucket_name)
  bucket_iterator = a_bucket.list_blobs()
  for resource in bucket_iterator:
    print(resource.name)
    limit = limit - 1
    if limit <= 0:
      break

my_list_bucket(BUCKET, limit=5)
Run Code Online (Sandbox Code Playgroud)

这会生成这样的输出.

UrbanSound/FREESOUNDCREDITS.txt
UrbanSound/UrbanSound_README.txt
UrbanSound/data/air_conditioner/100852.csv
UrbanSound/data/air_conditioner/100852.json
UrbanSound/data/air_conditioner/100852.mp3
Run Code Online (Sandbox Code Playgroud)

  • OP 要求类似“gsutil ls ...”的行为,该行为列出文件夹中的项目。您的代码递归地列出所有子文件夹中的所有项目。对于大型文件夹结构,您可能会得到比您预想的更多的东西! (2认同)

小智 5

要获取存储桶中的文件夹列表,您可以使用以下代码片段:

import googleapiclient.discovery


def list_sub_directories(bucket_name, prefix):
    """Returns a list of sub-directories within the given bucket."""
    service = googleapiclient.discovery.build('storage', 'v1')

    req = service.objects().list(bucket=bucket_name, prefix=prefix, delimiter='/')
    res = req.execute()
    return res['prefixes']

# For the example (gs://abc/xyz), bucket_name is 'abc' and the prefix would be 'xyz/'
print(list_sub_directories(bucket_name='abc', prefix='xyz/'))
Run Code Online (Sandbox Code Playgroud)

  • OP 确实要求使用 `google.cloud.storage` api... (2认同)