Google App Engineでは、通常のWebアプリケーションのように、サーバー上のファイルシステムへアクセスすることができない。別の方法でファイルの読み書きをする事になるけど、方法はいくつかあるので、それぞれの方法と長所短所をまとめてみた。サンプルと全てのソースコードはこちら。(サンプル/ソースコード)
方法1:BlobPropertyを使う
ndb(db)のBlobProperty(https://developers.google.com/appengine/docs/python/ndb/properties)ではバイナリデータをそのまま格納できる。まずは保存するModelの定義を実装する。
from google.appengine.ext import ndb
class UserFileModel(ndb.Model):
"""
ファイル格納モデル
"""
file_data = ndb.BlobProperty() # ファイルデータ
続いて、アップロードフォーム。
<form>
<div class="form-group">
<input id="exampleInputFile" name="file_data" type="file" />
</div>
<button class="btn btn-default" type="submit">保存</button>
</form>
アップロードハンドラを実装。リクエストされたデータをそのまま突っ込むだけ。
def post(self):
file_data = self.request.get('file_data')
user_file = UserFileModel()
user_file.file_data = file_data
user_file.put()
一番お手軽な方法だけど、1エンティティの上限が1MBまでという制限があるので注意が必要。それより大きいファイルを保存しようとするとこんなエラーが出る。
RequestTooLargeError: The request to API call datastore_v3.Put() was too large.
BlobPropertyではzip圧縮オプション(compressed=True)が使えるので多少容量を抑えることができる。ただ、圧縮しない時よりデータ読み書き時にCPUを消費するのでそこはトレードオフだ。
方法2:Blobstoreを使う
Blobstoreとは、Googleが提供するキー・バリュー型のデータストアサービス。上のBlobPropertyと名前が似てるけど、厳密にはGoogle App Engineとは別領域のサービスだ。まずはアップロード先URLの生成する。Blobstoreでは、最初にアップロード先のURLを生成しておく必要がある。生成にはgoogle.appengine.ext.blobstoreライブラリの create_upload_url を使う。
upload_url = blobstore.create_upload_url('/file_save/upload2')
引数の /file_save/upload2 は、アップロード後にリダイレクトされるパスを指定する。
続いて、アップロードフォームを実装する。上で生成したアップロードURLがPOST先となるようにする。
<form action="{{ upload_url }}" enctype="multipart/form-data" method="post" role="form">
<div class="form-group">
<input id="exampleInputFile" name="file_data" type="file" />
</div>
<button class="btn btn-default" type="submit">保存</button>
</form>
生成したアップロードURLはこんな感じになる。
http://gcp-memo.appspot.com/_ah/upload/AMmfu6aKr-VcNuFlEljprrh7rkbKnBA7WndhyciPTGolHiRkxVq670JENWihVEWViTxRue0K5Y10fOFpmnFzF4hn7qBMIZSn8vhmDT55aO5jNMPqEeQL46R6iWQvEps64_i8WQJsXAHl/ALBNUaYAAAAAVA_q4rzSwstBg1S6i5l3FpfHv4N0EdAR/
/_ah/upload はGoogle App Engineで決まっているシステム用のパスだ。ここでアップロードが行われた後、create_upload_urlで指定したパスに302リダイレクトされる。
アップロード後にリダイレクトされる処理を実装する。ここでBlobstoreに保存したファイルの情報が取れるので、キーを格納したりできる。このハンドラは、google.appengine.ext.webapp.blobstore_handlers.BlobstoreUploadHandlerクラス を継承しておく必要がある。
class Upload2(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file_data')
blob_info = upload_files[0]
self.redirect('/file_save/upload2_done?key=%s' % blob_info.key())
先にアップロードするURLを生成する必要があったり、アップロード後にリダイレクトされたりとちょっと扱いにくい印象。保存したファイルを参照する方法はまたの機会に。
方法3:Google Cloud Storageを使う
Google Cloud Storageは、Google Cloud Platformで提供されているストレージサービスだ。Google App Engineからも簡単にアクセスすることができる。アップロードフォーム。
<form action="upload3" enctype="multipart/form-data" method="post" role="form">
<div class="form-group">
<input id="exampleInputFile" name="file_data" type="file" />
</div>
<button class="btn btn-default" type="submit">保存</button>
</form>
アプロードハンドラを実装。通常のPythonアプリケーションの様に、ストレージ上のファイルの読み書きができる。ファイル操作には、Googleから提供されている Google Cloud Storage Client Libraryを使用する。(ダウンロードしてGAEアプリケーションに組み込んでおく必要がある)。
また、blobstore.create_gs_key 関数を使用すると、blobstoreライブラリで利用できるキーを生成することができる。blobstoreライブラリを使うと大きめのファイルのダウンロード処理が簡単にできるようになるので覚えておきたい。
関連ライブラリがごちゃごちゃしている感があるけど、通常のファイル操作と同じように扱えるため利用しやすい。
import lib.cloudstorage as gcs
# (中略)
class Upload3(Controller):
def post(self):
# ファイル名とファイルデータを得る
file_name = self.request.POST['file_data'].filename
file_data = self.request.get('file_data')
# このアプリケーションのデフォルトGCSバケット名を得る
bucket_name = app_identity.get_default_gcs_bucket_name()
# 保存パスを作成
filepath = '/' + bucket_name + '/file_save/' + file_name
# ファイル作成
gcs_file = gcs.open(filepath, 'w')
gcs_file.write(file_data)
gcs_file.close()
gcs_key = blobstore.create_gs_key('/gs' + filepath)
self.set_template_value('message', gcs_key)
self.draw_template('front/file_save/done.html')
まとめ
総合的に見るとGoogle Cloud Storageが一番良い。1MB未満のデータで無料枠で収まるようなアプリならBlobPropertyがお手軽でいいかも。Blobstoreを使うメリットは無さそうかな。方法 | 扱いやすさ | 容量制限 | ストレージ価格(GB/月) |
---|---|---|---|
BlobProperty | ◎ | 1MB | $0.18 |
Blobstore | × | 2GB | $0.026 |
Google Cloud Storage | ◯ | ほぼ無制限 | $0.026 |
大変参考になりました。
返信削除これからもいろんな情報アップしてくださいね♪