Google Cloud Storageのサブディレクトリ一覧を、GCP Pythonライブラリを使って取得したかったのですが、 ハマったので、その経緯と方法のメモ書きです。
他の言語のライブラリも、Pythonライブラリ同様APIラッパーなので、応用可能かと思います。
NG 例
最初、下記の方法でサブディレクトリが取得できないかなと試してみましたがダメでした。
from google.cloud import storage client = storage.Clinet() bucket = client.get_bucket('xxxx') dirs = bucket.list_blobs(prefix="a/", delimiter="/") [print(x) for x in dirs] # "a/"
ハマりポイント
Gooble Cloud Storageも、AWS S3同様、実態はファイルパスと中身からなる、巨大なKey・Valueデータベースです。
本当はディレクトリといったものはなく、パス(key)の途中で「/」を使うことによって、擬似的にディレクトリを表現しています。
なので、blob一覧取得の関数を使っても、そのままではうまくディレクトリが取得ができません。
うまく行ってる例
うまく行ってる方法もあるので見てみます。
GSUTIL
$ gsutil ls gs://xxxx/a/ gs://xxxx/a/ gs://xxxx/a/01/ gs://xxxx/a/02/
REST API
Try this APIで下記を設定
- bucket:xxxx
- delimiter:/
- prefix:a/
結果
{ "kind": "storage#objects", "prefixes": [ "a/01/", "a/02/" ], "items":[{ "kind":"storage#object", "id":"xxxx/a//1234567890", ...} ] }
うまく行ってるREST APIの解説および方針
bucket.list_blobs()は、上記のREST APIを呼び出しています。
欲しいのはREST APIの戻り値の「prefixes」ですが、blobとして返されるのは、「items」に格納されている、最初の「prefix」で指定した「xxxx/a/」のみです。
なので、bucket.list_blobs()が返すイテレータも「xxxx/a/」blobのみで、そのままでは「prefixes」を取得することができません。
bucket.list_blobs()は全てのblobが格納されますが、実際にはblob個数が大量の場合は、ページに分けて、複数回に渡ってREST APIをコールして取得しています。 そして、その1回分のレスポンスが、上記REST APIの戻り値になります。
なので「prefixes」は、まずはレスポンス、つまりページを取得して、そのページの「prefixes」プロパティから取得します。
そして、ページの情報は、bucket.list_blobs()が返すblobのイテレータの「pages」プロパディに格納されています。
「pages」はページのイテレータで、そのイテレータのアイテムの「prefixes」が、最終的に取得したいサブディレクトリ一覧になります。
以上を踏まえ、サブディレクトリ一覧の取得は下記になります。
手順
from google.cloud import storage client = storage.Clinet() bucket = client.get_bucket('xxxx') blobs = bucket.list_blobs(prefix="a/", delimiter="/") dirs = [] for page in blobs.pages: dirs.extend(page.prefixes) [ print(x) for x in dirs ] # "a/01/" # "a/02/"
感想
以前のAWS S3の時もそうだったのですが、ストレージにディレクトリというものがあると思い込んでしまうとハマります。
例えば、「Azure Blob」では、ディレクトリ内のファイルを全て消すとディレクトリも消えてしまうことから、そもそもディレクトリというものは無いことが分かります。
「AWS S3」「GCP Cloud Storage」「Azure Blob」どれも似たような仕様なので、ストレージのディレクトリはblob一覧ではなく、「delimiter」を「/」にした時のprefix一覧だと覚えておくといいかも知れません。
参考
- https://github.com/googleapis/google-cloud-python/issues/920#issuecomment-313384847
- https://stackoverflow.com/questions/51379101/how-to-get-list-blobs-to-behave-like-gsutil
関連記事
www.kwbtblog.com www.kwbtblog.com www.kwbtblog.com