GAE上で大きなサイズのファイルを処理する


##環境
* ruby歴 初心者
* rails5
* ruby 2.4.1p111

##これはなに？
GAE に大きなサイズ（数百MB等）のファイルを処理する方法を記載したものです。

※細かい部分でもコメントをいただけると学びになりますのでよろしくお願いしますm(_ _)m

##概要
まず、大きなファイルをどのようにアプリケーションに持っていくかで処理させる手法が変わってきます。
例えば、リクエストにファイルを乗せて、直接 GAE へのアップロードし処理する手法がまず思い浮かびますが
この手法ですと、制限が厳しく、特に下記の制限項目がまず壁となります。

・リクエストの処理が60秒を超えてはならない
・リクエストサイズが32MB以内

参考：
http://qiita.com/tomorier/items/7ee5222137651efddb33

そこで、今回、GAE に直接アップロードするのではなく
js から直接 GCS にアップロードし、アプリケーション側で、アップロードされたURLからデータをダウンロードして処理する手法を取りました。

##実装

まず、前提として、アップロードするためアクセストークンが必要になります。
アクセストークンの取得については[こちら](http://qiita.com/suna_suna/items/b607e900d3c36b6c0738)をご参照ください。
処理としては、下記のフローで行います。

1. ファイルを選択後、アップロードボタンを押下
2. ajax にてアクセストークンを取得
3. XMLHTTPヘッダーに必要な情報を付加
4. アップロード

まずは、アップロードに必要なものを json で返すようにします。
ちなみに、GCS へのアップロードリクエストについてアップロード専用のリクエストである [storage-upload.googleapis.com](https://cloud.google.com/storage/docs/xml-api/reference-uris) を使用すれば多少パフォーマンスが上がるようです。


```rb:uploader.rb
  def uploader_info

    key = "xxxx.zip"
    url = "https://storage-upload.googleapis.com/<バケット名>/#{key}"

    render json: { access_token: <アクセストークン>,
                   content_type: 'multipart/form-data',
                   upload_url: url,
                   redirect_url: <アップロード完了後のリクエスト先>,
                   redirect_params: {
                     storage_key: key
                   }
    }
  end

```

js側で、アップロードボタンを押下時に次の処理を走らせます。

```js:uploader.js

$(document).on 'ready', ->
  $(document).on 'click', '#js-upload-submit-btn', ->
	  $.getJSON(/uploader_info, (data) ->
	    xhr = new XMLHttpRequest();
	    xhr.open('PUT', data.upload_url);
	    xhr.setRequestHeader('Authorization', 'Bearer ' + data.access_token);
	    xhr.setRequestHeader('Content-Type', data.content_type);
	    xhr.onload = uploadSuccess.bind(this, xhr, data)
	    xhr.onerror = uploadFail.bind(this, xhr, data)
	
	    input_file = $(document).find('input[type=file]');
	    files = if input_file[0].files then input_file[0].files else [];
	    xhr.send(files[0])
	  )
	  
  uploadSuccess = (xhr, data) ->
	if xhr.status == 200
	  $.ajax
	    type: "POST",
	    url: data.redirect_url,
	    dataType: "script",
	    data: data.redirect_params,
	else
	  //エラー

  uploadFail = (xhr, data) ->
    //エラー


```

これでアップロードできるはずですが、このままだとエラーが発生します。

##CORS (Cross-Origin Resource Sharing)の適用

詳しい内容は[こちらの参考ページ](https://cloud.google.com/storage/docs/cross-origin)をご参照ください。
（今回が初めての操作だったので、そもそもこのセキュリテイ自を知らなかったです。。。。。）

ざっくりの解釈だと、開いているWebページから別のWebページへのアクセスを禁止するセキュリティのようで、このセキュリティのおかげで悪質なサイトからのリクエストなどを防御しているということでしょうか。

そして、そのセキュリティをコントロールする仕組みが `CORS (Cross-Origin Resource Sharing)` と言うもののようです。

こちらの説明を見つつ、バケットにCORS設定を行っていきます。
コマンドラインからバケットに対して下記のjsonで設定を行いました。

```json:cors.json
[
    {
      "origin": ["http://XXXXX.com"],
      "responseHeader": ["Authorization","Content-Type"],
      "method": ["PUT"],
      "maxAgeSeconds": 3600
    }
]

```

####■origin 
許可するホストを設定します。

####■responseHeader
今回は下記の２項目を追加します。

* Authorization
* Content-Type

####■method
今回は PUT のみにしました。

####■maxAgeSeconds
レスポンスをブラウザでキャッシュする時間を秒単位で指定するとうなので、お好み。

##結果
js から直接 GCS にアップロードすることができ、GAE の制限を回避してアプリケーションに大きなファイルを持ってくることができました！！！

##学んだこと

・GAEの制限について
・ブラウザのセキュリティ(同一生成元ポリシー)
・CORS(Cross-Origin Resource Sharing)

##参考
[GAE/Goで大きなファイルを扱う](http://qiita.com/tomorier/items/7ee5222137651efddb33)
[クロスオリジン リソース シェアリング（CORS）](https://cloud.google.com/storage/docs/cross-origin)
[Request URIs](https://cloud.google.com/storage/docs/xml-api/reference-uris)