Presigned URLの発行と取得したURLを使用して画像ファイルをGET/PUTする。

## 目的
コンテンツをサーバー上にアップロード・ダウンロードして配信するというユースケースがある。コンテンツを保持しておくサービスとして、AWSにS3というサービスがあるのでそれを利用する。

## 環境
- AWS Lambda Python 3.9

## Lambdaコード
```python
# -- coding: utf-8 --
import boto3
from botocore.client import Config
import os

REGION_NAME = os.environ['REGION_NAME']
S3_BUCKET_NAME = os.environ['S3_BUCKET_NAME']
DURATION_SECONDS = os.environ['DURATION_SECONDS']
KEY_NAME = os.environ['KEY_NAME']

s3 = boto3.client('s3', config=Config(signature_version='s3v4'))

def lambda_handler(event, context):
    
    put_presigned_url = s3.generate_presigned_url(
            ClientMethod = 'put_object', 
            Params = {
                'Bucket' : S3_BUCKET_NAME,
                'Key' : KEY_NAME,
            }, 
            # ExpiresIn = DURATION_SECONDS, 
            HttpMethod = 'PUT'
        )
    
    get_presigned_url = s3.generate_presigned_url(
            ClientMethod = 'get_object', 
            Params = {
                'Bucket' : S3_BUCKET_NAME,
                'Key' : KEY_NAME,
            }, 
            # ExpiresIn = DURATION_SECONDS, 
            HttpMethod = 'GET'
        )
    
    print('PUT Presigned URL: ', put_presigned_url)

    print('GET Presigned URL: ', get_presigned_url)

    return {
       'statusCode': 200,
       'statusDescription': '200 OK',
       'isBase64Encoded': False,
       'headers': {
           'Content-Type': 'text/html; charset=utf-8'
        },
        'body': ''
    }
```

## 実行環境の設定
### Lambda環境変数
以下、私の設定です。
- REGION_NAME=us-east-1
- S3_BUCKET_NAME=sample-s3-presignedurl
- DURATION_SECONDS=300
- KEY_NAME=upload/sample.jpg

![スクリーンショット 2022-03-02 22.14.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593337/e094769d-11a5-3633-c5b6-0ae6f49dd498.png)

S3_BUCKET_NAMEはGlobalで一意である必要があった気がするので変える必要があります。

### S3バケットの作成
環境変数で設定したS3_BUCKET_NAMEのバケット名でS3にバケットを作成する。

### IAM
LambdaのExecution RoleにS3へのPUTとGETを許すように変更する。
Lambdaの作成段階で割当たっているロールに、以下のようなポリシーをアタッチする。

```JSON
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::sample-s3-presignedurl/*"
        }
    ]
}
```

## 動作確認
Lambdaのテスト機能を利用して、実行し、PresignedURLを取得する。
PUTはcurlを叩くことでsample.jpgをアップロードする。
```sh
export url="<出力されたPUTのPresignedURL>"
curl -X PUT --upload-file sample.jpg $url
```
GETはブラウザに取得したPresignedURLを直打ちすることでダウンロードもしくは表示されることが確認できる。

## 詰まったところ
さまざまなサイトで、
```python
presigned_url = s3.generate_presigned_url(
            ClientMethod = 'get_object', 
            Params = {
                'Bucket' : S3_BUCKET_NAME,
                'Key' : KEY_NAME,
                'ContentType' : 'img/jpeg',
            }, 
            # ExpiresIn = DURATION_SECONDS, 
            HttpMethod = 'GET'
        )
```
と記載がありましたが、ContentTypeを指定すると、アップロードもダウンロードもできませんでした。。
ContentTypeを指定していたことが原因で、だいぶ時間がかかってしまいました。# もしかしたら、指定の仕方が悪かったのかもしれませんが。。
実は今回はECSで動いていたアプリ上のコードを検証のためにLambdaで動かしていた形になる。そのため、ECSで動かす時には、s3:PutObjectとs3:GetObjectの実行権限がタスクロールに割り当てられているかを確認する。

## 参考
https://dev.classmethod.jp/articles/create-pre-signed-url-with-lambda/

https://qiita.com/tmiki/items/87697d3d3d5330c6fc08