Google App Engine + Python アプリから Cloud Storage を利用する

※2016年7月4日に書いたブログ記事からの転記です。

# Cloud Storage を利用可能にしておく

まず最初に、Google Cloud Console から Cloud Storage を使用できるように初期設定しておく必要があります。  
初期設定は「利用開始」を選択して「バケットを作成」しておくだけです。  
デフォルトのバケットを作成してそれを使用してもよいですし、新たにバケットを作成してもOKです。

# Cloud Storafe クライアント・ライブラリをダウンロードする

App Engine Python アプリから Cloud Storage を利用するには、まず最初に Google Cloud Storage クライアント・ライブラリをダウンロードする必要があります。その方法は３つあります。

（１）Github から ZIP をダウンロードする：[Google Cloud Storage client library on GitHub](https://github.com/GoogleCloudPlatform/appengine-gcs-client/archive/master.zip)


（２）GitHub から clone する：

    git clone https://github.com/GoogleCloudPlatform/appengine-gcs-client.git

（３）PIP からインストールする：

    pip install GoogleAppEngineCloudStorageClient -t <your_app_directory/lib>

自分の場合は（１）の方法でダウンロードしました。  
ダウンロードしたファイルを解凍して、python → src → cloudstorage というフォルダを app_id/lib に配置します。

```
app_id/
  lib/
  cloudstorage/
```

さて、これで準備は整いました。  

# リクエストハンドラーを記述する

プログラムの大まかな流れは次のとおりです。

（１）ブラウザからファイルを受け取る。  
（２）受け取ったファイルを Cloud Storage へ保存する。  
（３）Cloud Storage での保存先を Datastore に保存する。  
（４）Cloud Storage に保存したファイルを表示する。  

まずはフォームの HTML です。

```HTML
<html>
  <body>
    <form method="post" action="" enctype="multipart/form-data">
      <label for="inputTitle">タイトル</label>
      <input type="text" name="title" id="inputTitle">
      <label for="inputFile">ファイル</label>
      <input type="file" name="file" id="inputFile">
    </form>
  </body>
</html>
```

次に、このフォームから送信されるデータを受け取る POST のリクエストハンドラーです。

```Python
from google.appengine.ext import ndb

from google.appengine.ext import blobstore
from lib import cloudstorage as gcs

# Cloud Storage に保存したファイルのパスを保持するための Datastore モデル
class File(ndb.Model):
    path = ndb.StringProperty()
    type = ddb.StringProperty()

class AdminCreateFileHandler(BaseHandler):
    def get(self):
        context = {}
        self.render_template('file.html', **context)  # 上記のフォームのHTMLを表示

    def post(self):
        title = self.request.get('title')
        file_data = self.request.get('file')
        file_name = self.request.POST['file'].filename
        file_type = self.request.POST['file'].type

        # フォームから受け取ったファイルをCloud Storageに保存
        if file_data:
            bucket_name = os.environ.get(
                "GCS_BUCKET_NAME",  # app.yaml に環境変数としてバケット名があればそれを使用
                app_identity.get_default_gcs_bucket_name())  # なければデフォルトのバケット名を使用

            file_path = '/' + bucket_name + '/files/' + file_name
            with gcs.open(file_path, 'w') as gcs_file:
                gcs_file.write(file_data)

        # Cloud Storage での保存先をDatastoreに保存
        file_key = File(
            path=file_path,
            type=file_type,
            title=title,
        ).put()

        self.redirect('/files/%s' % file_key.urlsafe())  # 適当なURLへリダイレクト
```

最後に、Cloud Storage に保存したファイルを表示するハンドラーです。

```Python
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers

class RequestHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, gcs_path):
      gs_key = blobstore.create_gs_key('/gs' + gcs_path)
      self.send_blob(gs_key)
```

このハンドラーを routes 内で 

```Python
Route(
    r'/gcs<gcs_path:/.+>',
    handler='handlers.gcs.RequestHandler',
)
```

といった感じのURLにマッピングしてあげます。


# ファイルを表示

ファイルを表示したい場合は、テンプレートに以下のように記述します。

```HTML
<img src="/gcs{{ file.path }}">
<!--file は File モデルのエンティティ-->
```

以上です。