CORSなJSだけでS3へのMultipartUL&DLする場合の必要な設定

# S3に対しCORSでJavascriptだけでマルチパートアップロードやダウンロードする場合の必要な設定

色々なサイトで値をアスタリスクで済ませてあるパターンが多くて、なんかモヤっとしたので、必要最小限は何か試してみた結果をここにすまし汁

## このTopicの前提条件
* IAMアカウントのキーでアクセスする
* ブラウザのから直接S3に対しアップロードやダウンロードを行う(javacriptのみ)
* アップロードはマルチパートアップロードを使用する

## S3のCORS設定
必要になる定義を以下に記載します
### AllowOrigin
クロスオリジンなので全てを許可せざるを得ないので値は*
`<AllowedOrigin>*</AllowedOrigin>`
### MaxAgeSeconds
お好みで、値は秒数。見本では2時間相当。
`<MaxAgeSeconds>7200</MaxAgeSeconds>`
### ExposeHeader
晒すレスポンスヘッダー、マルチパートアップロード等でブラウザとS3がやりとりするのに必要な値をレスポンスしてあげる必要がある。ETagとか。
`<ExposeHeader>ETag</ExposeHeader>`
`<ExposeHeader>x-amz-request-id</ExposeHeader>`
`<ExposeHeader>x-amz-id-2</ExposeHeader>`
### AllowedHeader
許可するヘッダーのように思えるが、実際には必要なヘッダーの定義。だと思う。
ここに必要なヘッダーが定義されていないとS3とのやりとりが失敗する。
`<AllowedHeader>Authorization</AllowedHeader>`
`<AllowedHeader>Content-Type</AllowedHeader>`
`<AllowedHeader>User-Agent</AllowedHeader>`
`<AllowedHeader>x-amz-date</AllowedHeader>`
`<AllowedHeader>x-amz-user-agent</AllowedHeader>`
`<AllowedHeader>x-amz-storage-class</AllowedHeader>`
`<AllowedHeader>x-amz-acl</AllowedHeader>`
User-Agentも必要でx-amz-user-agentも必要とか意味不明。
→値を見たら全く違う内容でした。つまりどちらも違う役割。
　x-amz-user-agentは「aws-sdk-js/2.0.0-rc13」みたいなSDKの種類やバージョンの値のようです。

### 必要ヘッダーの検証方法
AllowedHeaderの定義をアスタリスクにした状態でアップロード時のリクエストヘッダーをChromeの開発者ツールで観察し、そこに登場したヘッダー+「x-amz-storage-class」+「x-amz-acl」だけをAllowedHeaderとして編集してそこから一つづつ定義から消しながらマルチパートアップロードをトライアンドエラー。

結果、上記のヘッダー群が残りました。アカウント設定やらACLやらプログラムコードの書き方によっては違いが出てくるかもしれません。

### 最終結果のヘッダーまとめ

以下がS3へのJavascriptから直接ダウンロード＆アップロード(マルチパート)する場合のS3のコントロールパネルから行うCORS設定XMLです。

```
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <MaxAgeSeconds>7200</MaxAgeSeconds>
        <ExposeHeader>ETag</ExposeHeader>
        <ExposeHeader>x-amz-request-id</ExposeHeader>
        <ExposeHeader>x-amz-id-2</ExposeHeader>
        <AllowedHeader>Authorization</AllowedHeader>
        <AllowedHeader>Content-Type</AllowedHeader>
        <AllowedHeader>User-Agent</AllowedHeader>
        <AllowedHeader>X-Amz-Date</AllowedHeader>
        <AllowedHeader>X-Amz-User-Agent</AllowedHeader>
        <AllowedHeader>x-amz-storage-class</AllowedHeader>
        <AllowedHeader>x-amz-acl</AllowedHeader>
    </CORSRule>
</CORSConfiguration>
```


## javascript側のおおまかな流れ(マルチパートアップロード編)

詳しくはAWSのjavascript用SDKのドキュメントというかリファレンス見てください。

今回はアカウント情報とかノーガードな流れなので実装する際に気をつけてくだしあ

* フォームでファイルを選択させる
* 送信イベント発火(このタイミングで一旦ファイル情報だけPHPかなんかに送信してシグネチャーの生成とファイルのチェックとかすると良いのでしょう)
* ファイル情報を取得
* AWS.config.updateしたりS3インスタンス生成したり
* s3インスタンスのcreateMultipartUploadメソッド実行してマルチパート処理スタート。戻り値に重要なUploadIdがあるよ！
* ファイルをBlobとして分割送信サイズづつにsliceしながらループ処理でs3インスタンスのuploadPartメソッドを実行
* 戻り値にETagとかあるから値を配列に格納
* 全てのパーツの送信が終わったらs3インスタンスのcompleteMultipartUploadメソッドを実行※この時、引数のオブジェクトの中身にuploadPartメソッドの戻り値の塊が必要なのです。
* どこかでエラーが起きたらs3インスタンスのabortMultipartUploadを読んで中止しましょう。


12GBのファイルをマルチパートアップロードしてみたら、途中大量にエラー出たけど最終的にアップロード出来てました。だいたい1時間半位かかりました。
※2500分割位で送信して5000件以上エラー出てた。

## 追記

2015/12/10現在では自分は以下の様な設定で使っています。

```
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>HEAD</AllowedMethod>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <MaxAgeSeconds>7200</MaxAgeSeconds>
        <ExposeHeader>ETag</ExposeHeader>
        <ExposeHeader>x-amz-request-id</ExposeHeader>
        <ExposeHeader>x-amz-id-2</ExposeHeader>
        <ExposeHeader>x-amz-meta-custom-header</ExposeHeader>
        <AllowedHeader>Origin</AllowedHeader>
        <AllowedHeader>Authorization</AllowedHeader>
        <AllowedHeader>Content-Type</AllowedHeader>
        <AllowedHeader>User-Agent</AllowedHeader>
        <AllowedHeader>X-Amz-Date</AllowedHeader>
        <AllowedHeader>X-Amz-User-Agent</AllowedHeader>
        <AllowedHeader>x-amz-storage-class</AllowedHeader>
        <AllowedHeader>x-amz-acl</AllowedHeader>
        <AllowedHeader>x-amz-security-token</AllowedHeader>
    </CORSRule>
</CORSConfiguration>
```

いつの間にかExposeHeaderにx-amz-meta-custom-headerと
AllowedHeaderにx-amz-security-tokenを追加してました。
Cognito使うようになったからかな。。。

また、AllowedHeaderにOriginを追加しているのはこうしないとSafariの場合に
403エラーがでるので追加しています。仕組みは分かっていません。