今日は、Google Cloud Storage にファイルをupload するところを復習したので、そのメモを書きます。
準備
データはBucket というラベルの単位で管理します。ここにファイルをホイホイ入れたり出したりします。
簡単に使う分にはCloud Storage のクイックスタートをフォローするだけで十分です。
bucket の作成
コマンドラインからも作れますが、console.cloud.google.com から行ったほうが便利です。何度も作るものではないと思うので。
コンソールのStorage のブラウザから、「バケット作成」にたどり着けます。下記の画面では、私が作った(塗りつぶした)bucket のほかに、最初から xxxx.artifacts.projectid.appspot.com という名前のbucket がありました。これは開くと分かりますが、Cloud Build で作成したdocker image が置かれていました。(今回は関係ありません)
アクセス権の設定
どうやらbucket 単位でアクセス権を設定することができるようです。
全てのユーザに与えたり、特定にユーザにしたりできます。
個別のユーザだけでなく、allUsers, allAuthentificatedUsers が選べます。
おそらく入門ガイドにある「機能の使用」や「アクセスの制御」をじっくり理解しなければならないのだと思いますが、今回は精読をスキップして、以下のようにデフォルトのサービスアカウントに権限を割り当てて、そのアカウントのキーを使用しました。
サービスアカウントとキー
CREDENTIAL の扱い方については、いろいろな方法が用意されていますが、私はもともとこのプロジェクトにデフォルトで作成されているサービスアカウントを使うことにしました。
「IAMと管理」の「サービスアカウント」をみると、10000000000-compute@developer.gserviceaccount.com のようなCompute ENgine default service account がありました。
-
GCPで動かすときはこのアカウントが使われるらしい。ので、これに「ストレージオブジェクト管理者」のロールを与えます。さらに、作成したbucket にも権限を与えます。(←これは違うかも。今は、キーファイルを使って運用しています。。。) - ローカルで動かすときは、このサービスアカウントの鍵を作成し、これを環境変数GOOGLE_APPLICATION_CREDENTIALSに設定して利用します。
鍵は「サービスアカウント」のページの編集から鍵生成の画面に入れます。流れに従って鍵を生成すると myproject-0123456789012345.json のようなファイルが生成されるので、これを保存します。環境変数に設定します。
export GOOGLE_APPLICATION_CREDENTIALS=/...your_dir/myprojectid-0123456.json
サービスアカウントにbucket へのアクセス権を付与
bucket へのアクセスするには、使用するサービスアカウントがその権限を持っているかで制御します。今は、自分が作ったbucket に使用するサービスアカウントを追加しました。
先ほどと同じストレージブラウザで対象となるbucket を選択し、「バケットの権限を編集」を起動します。「ユーザの追加」から先ほどのサービスアカウントを選択し、「ロールの追加」でCloud Storage の「ストレージオブジェクト管理者」を選択して保存しました。
その他いろいろごちゃごちゃやったので、いまいちクリアにはなっていないので、もしかしたら少し違うかもしれませんが、私はこれで動いています。
サーバー間での本番環境アプリケーションの認証の設定を参考にしました。
python によるファイルの出し入れの実装
google-cloud-storage を pip でインストールします。
https://pypi.org/project/google-cloud-storage/
ここでは全てpython3 で実装と動作確認を行っています。
Storage にアクセスするClient
pythonの実装では、まず、Storage へのClient を作成します。
CREDENTIAL の設定ができていれば、以下でclient が作成されるはずです。直接にkeyファイルを指定するにはstorage.Client.from_service_account_json()
も使えますが、最近は上記のように環境変数を利用するほうがサーバとローカルとで同じ関数で書けるので使わなくなりました。
import google.cloud.storage as storage
from os import environ
def test_bucket():
print("GOOGLE_APPLICATION_CREDENTIALS={}".format(environ.get("GOOGLE_APPLICATION_CREDENTIALS")))
client = storage.Client()
for bucket in client.list_buckets():
print(bucket)
今一つbucket を作っていたので、以下のようになります。
GOOGLE_APPLICATION_CREDENTIALS=myserveraccountkey.json
<Bucket: asia.artifacts.my-project.appspot.com>
<Bucket: my-bucket>
file のupload
あとは、例文にあるように実装するだけです。が、APIの解説ではupload_from_file の引数がfile_obj ですが、私が動かしている範囲ではfile のpath を引数にしないとダメなようです。また、この状態だと、同じファイル名だと上書きを行っています。
import google.cloud.storage as storage
from os import path
def upload_file(local_fpath:str, bucket_name:str, remote_fpath:str):
client = storage.Client()
bucket = client.lookup_bucket(bucket_name)
assert isinstance(bucket, storage.bucket.Bucket)
bucket = client.get_bucket(bucket_name)
if not path.isfile(local_fpath):
return ""
blob = bucket.blob(remote_fpath)
if blob is not None:
blob.upload_from_filename(local_fpath)
url = "https://console.cloud.google.com/storage/browser/{}/{}".format(bucket_name, remote_fpath)
return url
return ""
file の download
同様にしてdownload もできます。ここでもdownload_to_fileの引数はファイルのパスで動いています。
import google.cloud.storage as storage
def download_file(bucket_name:str, remote_fpath:str, local_fpath:str):
client = storage.Client()
bucket = client.lookup_bucket(bucket_name)
blob = bucket.blob(remote_fpath)
if blob is not None:
blob.download_to_filename(local_fpath)
最低限のことはできるようになりました。
WEB APIの利用
実はpython API で実装している中身はREST APIを使って通信をしているらしいです。今回は、ファイルの存在だけ確認しました。使い方は、APIリファレンスのobject/listに書かれています。
GET https://storage.googleapis.com/storage/v1/b/<bucket-name>/o
解説ページでパラメータの動作確認ができる
この解説ページのある"try it"で簡単に試すことができます。
ここでは簡単に動きました。Storage のbucket の所定のprefix (フォルダ)にあるはずのファイル名が返ってきました。
OAUth 2.0 Playground (開発者向けの動確ページ)
ですが、実際にターミナルで動かすにはまた一苦労です。OAuth のaccess token が必要でした。
OAuthを利用したリクエストは、OAUth 2.0 Playgroundで試すことができます。token を作成し、http request を試すことができます。token 発行までの流れはここの手順に従っていけば良く、gcloud auth login と同じようにアカウントが認証します。
WEB APIで確認
ここで実行したリクエストをcurl でも動作することを確認しました。my-bucket-name というbucket にあるtest/dir01 以下にあるファイル名を取得します。access token は先に取得したもので、ya29.で始まる文字列でした。
curl 'https://storage.googleapis.com/storage/v1/b/my-bucket-name/o?prefix=test%2Fdir01' \
--header 'Authorization: Bearer ya29.accesstokenislooooooooooooooooong' \
--header 'Accept: application/json' \
--compressed
{
"kind": "storage#objects",
"items": [
{
"kind": "storage#object",
"id": "mqtt-log-test/test/dir01/my-filename/1234567890123456",
"selfLink": "https://www.googleapis.com/storage/v1/b/my-bucket-name/o/test%2Fdir01%2Fmy_filename",
"mediaLink": "https://storage.googleapis.com/download/storage/v1/b/my-bucket-name/o/test%2Fdir01%2Fmy_filename?generation=1588578408260925&alt=media",
"name": "test/dir01/my-filename",
"bucket": "my-bucket-name",
"generation": "1234567890123456",
"metageneration": "1",
"contentType": "application/x-tar",
"storageClass": "STANDARD",
"size": "192852",
"md5Hash": "vLES9qETsDyglngtaBsrdw==",
"crc32c": "XxXxXx==",
"etag": "CL3K493bmekCEAE=",
"timeCreated": "2020-05-04T07:46:48.260Z",
"updated": "2020-05-04T07:46:48.260Z",
"timeStorageClassUpdated": "2020-05-04T07:46:48.260Z"
}
]
}
便利なgsutil
実は急いでダウンロードしたときはgsutil を使っています。早くて便利だ。
gsutil -m cp gs://bucket_name/......./* .
所感
とりあえずファイルの出し入れはできるようになりました。また、権限の問題も最低限のところはクリアできた感じです。自分はpython の実装も十分に楽ですが、REST APIの方がもっと楽だという話も聞きます。
ぱらぱら見ていると、
- データを暗号化できる
- ユーザごとにアクセス権や暗号化キーを設定できる
など、APIドキュメントや解説を見ていても、いろいろ使い方あるようです。
ふー。
(2020/05/04)
追記
- (2020/05/05) 実際にbucket にファイルをupload するとき、"dir01/dir02/file"ならOKですが、"dir01/dir02//file" はNGでした。
- (2020/05/06) サービスアカウントにあった長い数字はPROJECT NUMBER らしい。これはシェルからは
gcloud projects describe ${PROJECT_ID} --format='get(projectNumber)'
で取得できる。 - (2020/05/06) 権限のところ、勘違いがあったかもしれないので、取り消し線を入れました。
- (20200615) gsutil を追加