クラウド

pythonでGoogle Cloud Storageへアクセスしてファイルをアップロード/ダウンロードする方法

pythonでgcsアクセス

webサービスを作成するに当たってGoogle Cloud Storageにpythonからアクセスしてファイルをアップロード/ダウンロードする方法を調べましたが、意外に時間を費やしました。

もちろん調べればそれなりに、いくつかサンプルが落ちているのですが、ライブラリ毎のAPIによって、動く場合と動かない場合があり試行錯誤しました。

本記事では、いくつかのパターンについて私が調べた方法についてまとめます。

準備・前提条件

Google Cloud Storageにアクセスするためにgoogle-cloud-storageをインストールします。

pip install google-cloud-storage

 

ファイルを保存したいbucketと保存ファイル名のblobインスタンスを取得します。(以下でバケット’bucket_name’は作成されている必要があります。)

from google.cloud import storage

#google cloud storageのクライアントインスタンスを作成
client = storage.Client()

#バケットのインスタンスを取得
bucket = client.bucket('bucket_name')

#ファイルのblobインスタンスを取得
blob = bucket.blob('image.png')

ローカルファイルの場合

ローカルファイルをGoogle Cloud Storageにアップロードする場合、もしくはそのままローカルファイルにダウンロードする場合は以下のように書きます。

ファイルを開いてアップロードする

#ファイルを開いてアップロードする場合 
#('rb'の'r'は読み込みモード、'b'はbinaryモードを表します。)
with open('image.png', 'rb') as image:
    blob.upload_from_file(image,  content_type='image/png')

※content_typeを指定しないと自動で「application/octet-stream」になります。

ファイルを開かないでアップロードする

#ファイルを開かないでアップロードする場合
blob.upload_from_filename('image.png')

 

ファイルを開いてダウンロードする

#ファイルを開いてダウンロードする場合 
#('wb'の'w'は書き込みモード、'b'はbinaryモードを表します。)
with open('image.png', 'wb') as image:
    blob.download_to_file(image)

 

ファイルを開かないでダウンロードする

#ファイルを開かないでダウンロードする場合
blob.download_to_filename('image.png')

 

 メモリ上のイメージの場合

実際に私が必要だったのは、メモリ上でイメージを生成して、アップロードするケース、ダウンロードしたファイルをメモリ上で処理するケースでした。この場合、テキストファイルかバイナリーファイルかで方法が異なります。

生成したテキストをアップロードする

#生成したテキストをアップロードする場合
blob.upload_from_string("これはテストファイルです。")

 

pandasのdataframeをcsvとしてアップロードする場合は以下のようになります。

#data_df (dataframe)をCSVファイルとしてアップロードする場合
blob.upload_from_string(
    data=data_df.to_csv(index=False), 
    content_type="text/csv")

テキストをダウンロードする

#テキストをダウンロードする場合
text = blob.download_as_string().decode()

※download_as_stringはバイト列なので、文字列にするためにはdecodeをする必要がああります。

csvファイルをdataframeとしてダウンロードする場合は以下のようになります。

#csvファイルをdataframeとしてダウンロードする場合
data_df = pd.read_csv(io.BytesIO(blob.download_as_string()))

生成したバイナリをアップロードする

生成したバイナリをアップロードするためには、io.BytesIO()を使用します。以下はpillowで生成したイメージをアップロードするサンプルです。

#バイトストリームを作成して、アップロードする。
buffer = io.BytesIO()
#以下のAPIがライブラリによって変わります。
#(xxx.tofile(buffer),xxx.write(buffer) 等)
img.save(buffer, "png")
#ファイルの先頭にシークします。
buffer.seek(0)
blob.upload_from_file(buffer)

 

pillowの場合は上記でうまく行きますがが、ライブラリによってはエラーになるケースがあります。この場合は、tempfileライブラリの機能を利用して、アップロードします。(tempfileの機能を使うとランダムなファイル名で一時ファイルを作成しますが、削除は明示的にする必要があります。)

使用するにはtempfileをインポートする必要があります。

#tempfileをインポートします。
import tempfile

 

使用する方法は以下となります。

_, temp_local_file = tempfile.mkstemp(suffix=".png")
#以下のAPIがライブラリによって変わります。
#(xxx.tofile(buffer),xxx.write(buffer) 等)
img.save(temp_local_file)
blob.upload_from_filename(temp_local_file)
#一時ファイルのため削除する。
os.remove(temp_local_file)

※pillowの場合はBytesIO()で動作しますが、比較のためpillowのイメージで記載しています。(pillowはどちらの方法でも動作します。)

バイナリをダウンロードする。

#生成したバイトストリームにダウンロードして開く。
buffer = io.BytesIO()
blob.download_to_file(buffer)
#以下のAPIがライブラリによって変わります。
#(xxx.load(buffer),xxx.read(buffer) 等)
img = Image.open(buffer)

 

上記方法でエラーになるライブラリの場合は、アップロード時と同様にtempfileを使用します。

_, temp_local_file = tempfile.mkstemp(suffix=".png")
blob.download_to_filename(temp_local_file)
#以下のAPIがライブラリによって変わります。
#(xxx.load(buffer),xxx.read(buffer) 等)
img = Image.open(temp_local_file) 
#一時ファイルを削除する。
os.remove(temp_local_file)

※pillowの場合はBytesIO()で動作しますが、比較のためpillowのイメージで記載しています。(pillowはどちらの方法でも動作します。)

番外編 gcsfsを利用する方法

ライブラリによっては、gcsfsを利用することによって、あたかもGoogle Cloud Storageをファイルシステムのように使用する事ができます。

使用するためには、gcsfsをインストールします。

pip install gcsfs

 

pandasではgcsfsを利用する事ができ、アップロード、ダウンロードはそれぞれ以下のように書きます。

アップロードする方法

#storageにアップロードする。
df.to_csv("gs://bucket_name/data.csv", index=False)

 

ダウンロードする方法

#storageからダウンロードする。
data_df = pd.read_csv("gs://bucket_name/data.csv")

 

ただしライブラリによって上記方法が使える場合と使えない場合があるので注意が必要です。

まとめ

以上、Google Cloud Storageにアクセスしてファイルをアップロード/ダウンロードする方法について、いくつかのパターンを記載しました。

おそらく、上記のいづれかのパターンを用いれば、アップロード、ダウンロードが可能なはずです。

全てgcsfsのやり方でできれば簡単で良いのですが・・