AWS+Reactアプリ作成入門(S3編）

[AWS+Reactアプリ作成入門(Cognito編)](https://qiita.com/sand/items/e1f4ec830d13569a7128 "AWS+Reactアプリ作成入門(Cognito編)")
[AWS+Reactアプリ作成入門(S3編）](https://qiita.com/sand/items/94cd85c3c0426dc768bd "AWS+Reactアプリ作成入門(S3編）")
[AWS+Reactアプリ作成入門(DynamoDB編）](https://qiita.com/sand/items/70018f322d6fe986fe60 "AWS+Reactアプリ作成入門(DynamoDB編）")
[AWS+Reactアプリ作成入門(IAM Role編）](https://qiita.com/sand/items/a07a08f7c1ccc2e8b967 "AWS+Reactアプリ作成入門(IAM Role編）")
[AWS+Reactアプリ作成入門(ログイン後のAdmin編)](https://qiita.com/sand/items/260a6a0b360e574642cf "AWS+Reactアプリ作成入門(ログイン後のAdmin編)")


今回作成したアプリ ==>[久喜SNS](http://kuki-app-bucket.s3-website-ap-northeast-1.amazonaws.com/ "久喜SNS")


　前回はユーザ認証（Cognito）周りを中心に述べましたが、今回はS3の利用について述べたいと思います。S3は２つの目的で使います。画像の保存とReactアプリの保存です。Reactアプリの開発時は別サーバで行い、完成したらS3にdeployします。

　まずAWSコンソールでS3にバケットを作成します。Reactアプリを走らせますので静的ホスティングをONにします。プログラムをdeployした時に誰でもアクセスできるように、以下のようにバケットポリシーを設定します。これで誰でもRead Onlyでプログラムにアクセスでき、ホスティングのページに示されたURLにアクセスするとプログラムが起動されます。Cognitoでプログラムの実行に権限を与えることはできますが、バケットポリシーはプログラムの実行以前の、プログラムのロードに必要となります。プログラムから画像をアップロードしたり削除したりするCognitoの権限(Role)については別の機会にしまします。

```json:バケットポリシー
{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Sid":"AddPerm",
      "Effect":"Allow",
      "Principal": "*",
      "Action":["s3:GetObject"],
      "Resource":["arn:aws:s3:::examplebucket/*"]
    }
  ]
}
```

[公式サイト：Granting Read-Only Permission to an Anonymous User](https://docs.aws.amazon.com/AmazonS3/latest/dev/example-bucket-policies.html "公式サイト：Granting Read-Only Permission to an Anonymous User")



　Cognitoは面倒でしたが、S3はシンプルです。信頼性が高く低価格なので、認証が不要であれば、簡単にReactアプリを作成し、以下のコマンド一発でdeployすることが可能です。react-create-appで作成したプロジェクトのディレクトリーで実行します。この辺がMeteorにない魅力ですね。

```sh:
npm run build && aws s3 sync build/ s3://バケット名
```

　このコマンドを使うためには予めaws-cliを設定しておく必要があります。ついでですのでその手順を載せておきます。

# １．AWS-CLIのインストール

　公式サイト「AWS CLI のインストールと設定」に従って行います。
http://docs.aws.amazon.com/ja_jp/streams/latest/dev/kinesis-tutorial-cli-installation.html

（１）インストール
　次のコマンドでインストールできます。

```sh:
curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
sudo python get-pip.py
sudo pip install awscli
aws help
```

（２）設定
　まずAWSコンソールのIAM画面のユーザで自分を検索し、アクセスキーとシークレットキーの入手します。認証情報タブでアクセスキーの作成をクリックするとポップアップにシークレッドキーが表示されますので、忘れずにコピーしておいてください。

```sh:
aws configure
AWS Access Key ID [None]: AKIAXXXXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXX
Default region name [None]: ap-northeast-1
Default output format [None]: json
```

（３）確認
　念のため確認しておきます。これでaws-cliが使えるようになりました。

```sh:
$ cat ~/.aws/credentials
[default]
aws_access_key_id = AKIAXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXX

$ cat ~/.aws/config
[default]
output = json
region = ap-northeast-1
```

# ２．画像のアップロード
　ReactアプリからS3にアクセスするためには権限が必要となります。権限は非ログインユーザとログインユーザにそれぞれ付与されます。そのためにはCognitoの設定が必要となりますが、これについては前回述べましたので参照してください。

　まずファイル選択ですが以下のJSXで行います

```js:ファイル選択ボタン
<input type="file" onChange={this.handleFileChange.bind(this)}/>
```


　this.state.fileに選択したファイルを保存します。e.target.files[0]で選択したファイルにアクセスできます。

```js:ファイル選択ハンドラー
  handleFileChange(e) {
    this.setState({file: e.target.files[0]});
  }
```

　送信ボタンを押された時のハンドラーの一部です。通常callbackの中で処理を行いますので、予め _self=this としていると仮定してください。s3.putObject()で画像ファイルをアップロードしています。一応 Metadata も設定していますが、今回は使っていないので不要です。

```js:送信ボタンのハンドラの一部
        if (!_self.state.file.name.match(/^[0-9a-zA-Z\.\_\-]*$/)) {
            handleErrorFunc('エラー：ファイル名は小文字の英数字と . - _ しか使えません: '+_self.state.file.name );
            return;
        }
        filename = 'contents/images/'+identityId+'/'+_self.state.file.name;
        fileType = _self.state.file.type;
        var params = {
            Bucket: bucketname,
            Key: filename,
            ContentType: _self.state.file.type,
            Body: _self.state.file,
            Metadata: {
              data: JSON.stringify({
                identityId: identityId,
                uploadTime: uploadTime,
                uploadDate: uploadDate
              })
            }
        };
        var s3 = new AWS.S3();
        s3.putObject(params, function(err, data) {
            if(err) {
                console.log("Err: upload failed :" +err);
            } else {
                console.log("Success: upload ok");
            }
        });
```


# ３．画像の参照
　上で作成したfilename変数を使えば、画像は以下のURLで参照できます。実際はfilenameはDynamoDBのテーブルに保存してあるので、そこから取り出してURLを作成することになります。

```
const imgurl = 'http://'+bucketname+'.s3-'+appConfig.region+'.amazonaws.com/'+filename;
```

# ４．画像の削除
　画像の削除も簡単です

```js:削除ボタンのハンドラの一部
        const s3 = new AWS.S3();
        const params1 = {
            Bucket: bucketname,
            Key: item.filename,
        };
        s3.deleteObject(params1, function(err, data) {
          if (err) console.log(err); // an error occurred
          else     console.log("### delete image ok");           // successful response
        });
```

# ５．キャッシュファイル
　DynamoDBは課金制ですし、安いスループットに設定しているので、トップページにキャッシュを設けることにしました。これは３番目のS3の使い方になります。

　トップページは常に現在の投稿の一覧を取得し表示してますので、DyanmoDBにqueryを発行しています。この投稿の一覧をJSONファイルとしてS3にキャッシュします。新たな投稿や編集があった場合にキャッシュを削除します。

```js:キャッシュファイルの作成
    var params = {
        Bucket: bucketname,
        Key: cachepath,
        ContentType: 'application/json',
        Body: JSON.stringify(data.Items)
    };
    s3.putObject(params, function(err, data) {
      if (err) console.log("### cache upload error",err, err.stack);
      else     console.log("### cache upload successful"+data);
    });
```

キャッシュの削除や参照については割愛します。


今回はこれで終わりますが、DynamoDBなどは次回以降に書いていきたいと思います。