API Gateway+Lambdaでバイナリデータを処理

# 1. まとめ

クライアントからAPI GatewayにPOSTされたバイナリデータをLambdaで何かしらの処理を実施する方法を記載する．ここでは，実例としてバイナリデータとして画像ファイルを受け取り，Lambdaにて画像をS3に保存する方法を記載する．

# 2. 目次

- 1 まとめ
- 2 目次
- 3 簡易処理フロー
- 4 実装
    - 4.1 Lambda
    - 4.2 API Gateway
- 5 テスト
- 6 参考リンク

# 3. 簡易処理フロー
- API Gateway
    - バイナリメディアタイプにてimage/*を設定しバイナリを扱えるよう設定
- Lambda
    - Base64にエンコードされたデータをデコードし画像ファイルのバイナリーデータを取得
    - 画像をS3に保存
- S3
    - 画像を格納

# 4. 実装
　
下記のcURLで叩いて，S3に画像が保存されていればOK

```bash
$ curl -X POST -H 'Content-type: image/jpeg' --data-binary "@ramen.jpeg" https://xxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1/upload
```

![ramen.jpg](https://qiita-image-store.s3.amazonaws.com/0/262328/9d5091f1-d4f5-fdf9-e121-71f12e69f7cf.jpeg)

## 4.1. Lambda

### コード全体

```javascript
'use strict';

const aws = require('aws-sdk');
const s3 = new aws.S3({ apiVersion: '2006-03-01'});

exports.handler = (event, context, callback) => {

    // add logging level
    console.log = console.log.bind(null, '[LOG]');
    console.info = console.info.bind(null, '[INFO]');
    console.warn = console.warn.bind(null, '[WARN]');
    console.error = console.error.bind(null, '[ERROR]');

    // variables
    var response = {}

    // check event
    console.log(event)

    // if data is encoded with base64, then decode with base64 and save it in S3
    if (event.isBase64Encoded === true) {

        // decode
        var decode = new Buffer(event.body,'base64');

        // save data in s3
        var params = {
                        Bucket: 's3-xxxxxxxxxxxxxxx-20180702', 
                        Key: 'ramen.jpg', 
                        ContentType: 'image/jpeg', 
                        Body: decode
                    };
        s3.putObject(params, function(err, data) {
            if (err) {
                console.error(err);
                console.error('image upload failed');

                // create response
                response.statusCode = 400
                response.body = JSON.stringify({
                    message: 'fail.'
                })
                callback(null, response);

            } else {
                console.log('image upload success');
    
                // create response
                response.statusCode = 200
                response.body = JSON.stringify({
                    message: 'image has been uploaded to S3'
                })
                callback(null, response);
            }
        });

    } else {

        // create response
        response.statusCode = 400
        response.body = JSON.stringify({
            message: 'fail.'
        })
        callback(null, response);
    }
};
```

### 補足

#### eventの中身の確認

下記でCloudWatchにログとして書き出す

```javascript
    // check event
    console.log(event)
```

下記が結果．重要な部分だけ抽出．
①`event.body`がbase64でencodeされた画像データ
②`event.isBase64Encoded`がtrueになっておりbase64でバイナリデータをエンコードされているのがわかる

```
body: '/9j/4AAQSkZJRgABAQAASABIAAD/99b06vJCFQ+N .. 省略 ... h/9k=',
isBase64Encoded: true 
```

### デコード

base64でエンコードされていることを確認し，デコード`new Buffer(event.body,'base64')`を実施

```javascript
    // if data is encoded with base64, then decode with base64 and save it in S3
    if (event.isBase64Encoded === true) {
        // decode
        var decode = new Buffer(event.body,'base64');
```

## 4.2. API Gateway

### バイナリメディアタイプを設定しない場合のレスポンス

```bash
$ curl -X POST -H 'Content-type: image/jpeg' --data-binary "@ramen.jpg" https://xxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1/upload

{"message":"fail"}
```

バイナリメディアタイプに`image/*`を追加し変更を保存．ステージに反映するには再度APIをデプロイする必要がある．
![binary_media_type.jpg](https://qiita-image-store.s3.amazonaws.com/0/262328/b03cd3f7-68a0-7197-29e4-06f3d0f2b6b5.jpeg)

### バイナリメディアタイプを設定した場合のレスポンス

`image/*`が登録されているため，`image/jpeg`が通る．

```bash
$ curl -X POST -H 'Content-type: image/jpeg' --data-binary "@ramen.jpg" https://xxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1/upload

{"message":"image has been uploaded to S3"}
```

適当に`image/*`以外にContent-typeを設定してみると，エラーになる．想定通りの挙動

```bash
$ curl -X POST -H 'Content-type: text/plain' --data-binary "@ramen.jpg" https://xxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1/upload

{"message":"fail"}
```

Content-typeを外してみてもエラーになる．想定通りの挙動

```bash
$ curl -X POST --data-binary "@ramen.jpg" https://xxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1/upload

{"message":"fail"}
```

# 5. テスト

cURLで叩いてみる

```bash
$ curl -X POST -H 'Content-type: image/jpeg' --data-binary "@ramen.jpg" https://xxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1/upload

{"message":"image has been uploaded to S3"}
```

S3に保存されていることを確認

![S3_upload.png](https://qiita-image-store.s3.amazonaws.com/0/262328/2b1d39fd-495e-6e86-986c-fcaba444da76.png)

# 6. 参考リンク
なし