API Gateway + LambdaでS3にある画像を表示

## 概要

* API GatewayとLambdaでS3にある画像を返却する
* クライアント側からは単にURLにアクセスしたら画像が表示されるように見える
* S3をpublicにすることなく画像を表示することができる (限定公開などが可能)
* pythonでの実装

仕様がよく分からずハマったのでメモを残します．

## やること

1. Lambdaの作成
2. API Gatewayの作成

S3は作成済みとします．

## 1. Lambdaの作成

Lambdaを適当な名前で作成します．ここでは`get_image`としました．

また，ランタイムはPython 3.8を選択しました．

![スクリーンショット 2020-06-27 22.24.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/77615c8f-9cac-814a-7709-f5702e3b44dc.png)

そして関数コードに以下のコードを登録します．

```python
import boto3
import base64

def get_img_from_s3():
    s3 = boto3.client('s3')
    bucket_name = 'BUCKET_NAME'
    file_path = 'FILE_PATH'
    responce = s3.get_object(Bucket=bucket_name, Key=file_path)
    body = responce['Body'].read()
    body = base64.b64encode(body)
    return body
    

def lambda_handler(event, context):
    img = get_img_from_s3()
    return img
```

`bucket_name`にS3のバケットの名前，`file_name`に読み込みたいS3のオブジェクト(画像)のパスを渡しS3からオブジェクトを読み込みます．

その後読み込んだバイナリ形式のオブジェクトをbase64にエンコードして返却します．


### LambdaにS3へのアクセス権限を付与

作成したLambdaはS3へのアクセス権限を持っておらず，このままだとアクセスが拒否されてしまうのでS3へのアクセス権限を付与します．

まず作成したLambdaのアクセス権限のページを開きます．
![スクリーンショット 2020-06-27 23.07.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/b76d8d86-b028-1696-54c5-6416a4cef326.png)
ここに自動で作成された実行ロールが割り当てられているので，この実行ロールのページを開きます．
![スクリーンショット 2020-06-27 22.47.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/e297b702-e0b0-926a-cf0a-84f59ed24a4d.png)
ページ中央の青い`ポリシーをアタッチします`というボタンを押します．
![スクリーンショット 2020-06-27 22.50.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/755b90c7-4ded-b9e8-506f-d010266a26c4.png)
このようにいろいろなポリシーが出てきます．
![スクリーンショット 2020-06-27 22.51.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/b2c42536-fdf2-7a41-7817-fcf4a4da84eb.png)
`S3`で検索するとS3に関するポリシーが出てきます．
今回はS3から画像を読み込むので`AmazonS3ReadOnlyAccess`というポリシーをアタッチします．
これでLambdaからS3に保存されているファイルを読むことができるようになります．

以上でLambdaの設定は終了です．

## 2. API Gatewayの作成

続いてAPI Gatewayの作成をします．

![スクリーンショット 2020-06-27 23.17.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/98bd909c-e4cb-af44-b3ee-e57b495b036b.png)

APIタイプはREST APIを選択し，API名は適当に`get_image`としました．
![スクリーンショット 2020-06-27 23.19.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/4cc771f7-26a1-c44d-bdc6-86f02c75aff1.png)
続いてGETメソッドを追加し，統合タイプにはLambdaを，Lambda関数には先ほど作成した`get_image`というLambda関数を指定しました．

これでAPI Gatewayが作成できたのでメソッドレスポンスの設定を行います．
![スクリーンショット 2020-06-27 23.25.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/50643e6d-b19e-0a88-fc79-c49162781c9c.png)
まずレスポンスヘッダーに`Content-Type`を追加し，レスポンス本文のコンテンツタイプには`image/png`を追加しました．(今回はpngを読み込んだので`image/png`にしました．)

次に統合レスポンスの設定を行います．
![スクリーンショット 2020-06-27 23.31.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/bf1429e1-e3bc-1ead-2665-46ff75d8a842.png)

コンテンツの処理方法をデフォルトではパススルーになっていますが，`バイナリに変換（必要な場合）`を選択します．
またヘッダーのマッピングのマッピングの値を`'*'`とします．

以上でAPI Gatewayの設定も終了です．

最後にAPIのテストを行うと
![スクリーンショット 2020-06-27 23.42.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/06bd901d-54af-65ac-f2a9-bac55e8640a0.png)
このようにpngのバイナリを返すことができました！

実際にAPIをデプロイしてURLにアクセスすると画像が表示されます．



## 参考

Node.jsによる実装　[Lambda + API Gateway入門。画像のDL](https://xp-cloud.jp/blog/2018/07/12/3876/)