AWS lamdaからS3にファイルをアップロードする

いろいろなやり方があるようで、右往左往してしまったので、その経緯を備忘録的に残します。

結論として、
* API gatewayでマッピングテンプレートを設定する
* API gatewayでバイナリメディアタイプを設定する
* lambdaロールにS3へのアクセス権限を付与する
がポイントのようです。

## lambda関数の作成

pythonで作成します。

1. AWS lambdaから関数の作成へ
![スクリーンショット2022-10-1515.50.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/5668787d-0900-53ad-f7cc-3e961e332c8b.png)

1. 任意の関数名を入力、python 3.8を選択して関数を作成します。
![スクリーンショット 2022-10-15 16.01.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/ebbf3a23-aa22-1b6c-53f8-dd4199e8d2e6.png)

1. 参考記事のままですが以下のコードを使用しました。関数名はuploadFilesとしています。

```:uploadFiles
import json
import boto3
import base64
import io
from datetime import datetime
import cgi

BUCKET_NAME='mybucket22021015'
DIRECTORY='uploadedfiles/'

s3 = boto3.resource('s3')

def lambda_handler(event, context):
    bucket = s3.Bucket(BUCKET_NAME)
    
    body = base64.b64decode(event['body-json'])
    #body = event['body']
    fp = io.BytesIO(body)
    
    environ = {'REQUEST_METHOD': 'POST'}
    headers = {
        'content-type': event['params']['header']['content-type'], 
        'content-length': len(body)
    }

    fs = cgi.FieldStorage(fp=fp, environ=environ, headers=headers)
    for f in fs.list:
        print("filename=" + f.filename)
        bucket.put_object(Body=f.value, Key=DIRECTORY+f.filename)

    return {
        'statusCode': 200,
        'body': json.dumps('アップロード完了')
    }

```

## API gatewayの作成

関数URLではなくAPIgatewayを使用します。

1. API gatewayからAPIを作成をクリック]
![スクリーンショット 2022-10-16 10.27.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/a89fe670-45d9-b9fa-e1d9-b71b6a132a7a.png)

1. REST APIを構築します
![スクリーンショット 2022-10-16 10.29.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/59431e26-a5e9-601a-530f-cfe7a8306c1a.png)

1. 任意のAPI名を入力してAPIの作成をクリック 
![スクリーンショット 2022-10-16 10.30.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/6f809103-8e2e-8875-6c43-f1958054b0fa.png)

1. アクションからメソッドの作成。作成されたフィールド右端の下向き三角みたいな記号をクリックするとメソッドを選択できる。クライアント側からのデータを取得できるようにPOSTを選択する。フィールド横のチェックマークをクリック
<table>
<tr>
<td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/fee33ec2-2478-08f7-36b6-8ac5ac0a28e6.png"></td>
<td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/3b92d9c5-96cf-8133-f068-15aaba88d665.png"></td>
<td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/ec66728b-3402-68ab-0467-0309f47fd766.png"></td>
<td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/9ddcb0a2-44ba-c549-5d79-e5b14ea3549a.png"></td>
</table>

1. lambda関数を選択し、上記で作成したlambda関数のリージョン、関数名を入力して保存をクリック 
![スクリーンショット 2022-10-16 10.48.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/e323c422-ccb1-5fc8-07f5-ba48851a2771.png)

1. デプロイの仕方：アクションからAPIのデプロイを選択します。適当なステージ名を入力してデプロイしましょう。URLの呼び出し欄に表示されるURLでアクセスすることができるようになります。
![スクリーンショット 2022-10-16 11.00.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/19432d4c-f395-dd57-0d5e-0953c5be7f84.png)
![スクリーンショット 2022-10-16 11.02.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/0f5ce114-5c60-f3e8-e8e7-7da29a211bbb.png)

## 動作確認１

動作確認はGoogleChrome拡張機能のTalend API Testerを用いました。
下記のように入力した後に、Sendボタンを押します。
![スクリーンショット 2022-10-16 11.15.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/2850ec67-a85c-ef86-38b7-8eca7338031e.png)

ここまでの設定ではうまくいきませんでした。Content-Type:multipart/form-dataで受け取れるようにするにはAPI gatewayでマッピングテンプレートを設定する必要があるようです。

## API gateway　マッピングテンプレートの設定

1. 先に設定したPOSTメソッドの「統合リクエスト」をクリックします。
![スクリーンショット 2022-10-16 11.29.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/873ff892-f2a8-6f0c-9197-c49161861413.png)

1. 「マッピングテンプレート」をクリックします。
![スクリーンショット 2022-10-16 11.33.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/3a358078-ee60-206e-b1e6-fa4c4528ffe0.png)

1. 推奨の「テンプレートが定義されていない場合」のラジオボタンを選択し、「マッピングテンプレートの追加」をクリックします。
![スクリーンショット 2022-10-16 11.34.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/bb96f567-e9a8-79f3-b9f7-bc08f217b231.png)

1. Content-Typeにmultipart/form-dataを入力し、チェックマークをクリックします。
![スクリーンショット 2022-10-16 11.36.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/cfba82ad-ea68-8f04-5c8f-0e76248e7c99.png)

1. テンプレートの生成の右端にある下向き矢印をクリックします。
![スクリーンショット 2022-10-16 11.38.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/89d8f5bb-2a42-23ae-2962-b22d7c5bc01c.png)

1. 次いで、「メソッドリクエストのパススルー」を選択するとテンプレートが自動入力されます。特に編集しないまま保存ボタンをクリックしましょう。
![スクリーンショット 2022-10-16 11.41.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/8d3eba6d-aaf5-0b70-b062-d393f2e6cfd8.png)


1. これでマッピングテンプレートの設定は完了です。念の為、再度デプロイしました。

## 動作確認２

動作確認１と同様にテストしましたが、やはりエラーとなりました。エラーメッセージは、
`string argument should contain only ASCII characters`とのこと。


## API gateway バイナリメディアタイプの設定

さらにAPI gatewayの設定が必要でした。

1.API gatewayのナビゲーションバーより、「設定」をクリック
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/0dcba549-1396-b0a5-f0cb-f581a419e206.png" width="200">


1. 一番下にスクロールしてバイナリメディアタイプを追加します。
1. ![スクリーンショット 2022-10-16 12.01.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/85a29e7c-6be5-201e-5598-8ea3c7a746ba.png)

1. 「multipart/form-data」を入力して、変更の保存ボタンをクリックします。再度、デプロイしておきましょう。
![スクリーンショット 2022-10-16 12.05.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/da1b27fa-d5d3-a792-2563-b58241d99ae5.png)

## 動作確認３

まだエラーとなります。エラーメッセージは、
`An error occurred (AccessDenied) when calling the PutObject operation: Access Denied`

lambdaからS3を操作する権限がないので怒られている感じですね。

## lambda ロールの設定

1. lambdaの設定タブから「アクセス権限」をクリックします。
![スクリーンショット 2022-10-16 13.00.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/030a0370-333c-b731-dc7b-f40514a37d3c.png)

1. lambda関数に設定されているロールを確認できます。これをクリックするとIAMが開きます。
![スクリーンショット 2022-10-16 13.38.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/2d65801e-8ee2-01e6-51d0-3734a557351d.png)

1. 許可を追加ボタンからポリシーをアタッチすることができます。「インラインポリシーを作成」からの方がわかりやすいかと思います。
![スクリーンショット 2022-10-16 13.42.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/321e19e2-2b16-098d-4e16-5e1094fc45cb.png)

1. 「インラインポリシーを作成」をクリック後に現れる画面で、サービスにS3を選択します。アクションについては、今回はListBucket、GetObject、PutObjectの３つを設定しました。
![スクリーンショット 2022-10-16 13.59.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/a12c8c7e-5752-6017-8513-82a84af8281c.png)

1. 各アクションの対象となるリソースを指定する必要があります。予め準備しておいたS3バケットを設定します。

　　バケットの設定は下記のようにバケット名（今回はtestbucket）
![スクリーンショット 2022-10-16 14.03.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/92d9dc39-f8f9-58fb-15fc-7e9536ae7484.png)

　　オブジェクトの設定は、今回はtestbucket以下すべてとしました。
![スクリーンショット 2022-10-16 14.06.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/45300ed4-c115-21e3-c343-eed450a5c599.png)

1. 設定できたらポリシーの確認ボタンを押します。
![スクリーンショット 2022-10-16 14.08.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/4828d2b5-9351-23ff-9d02-6295babb0be2.png)

1. ポリシー名を入力してポリシーの作成ボタンを押します。
![スクリーンショット 2022-10-16 14.10.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/32a3bfa6-50a8-7d3c-bfb7-75a7d4fece97.png)

1. 作成したポリシーがlambda関数に設定されていることがわかります。（今回はポリシー名をqiitaとしました）
![スクリーンショット 2022-10-16 14.12.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152192/884600bf-a408-016e-c77a-ea0e414843c7.png)

## 動作確認４

以上の設定で、任意のファイルをlambda経由でS3にアップロードできることが確認できました。