AWS Lambda(Python 3.8) + S3でファイルをアップロードする

今回は、サーバレス(AWS Lambda(Python 3.8) + S3)でファイルをアップロードする仕組みが必要となったため、その時に調べた方法の備忘録です。

Spring boot(EC2+RDS)でWebアプリケーションとして作成する予定だったのですが、コスト面で見直した結果、今回のような構成で行くことにしました。 LambdaやAPI Gatewayは初めて使っみてたのですが、当初思っていたよりは簡単に利用できて驚きました。 今回アクセスコントロール周りには触れていないのであしからず...

Lambda関数の作成

まずはLambda関数を作成します。 コンソールでLambdaを選択し、関数の作成から新しいLambda関数を作成します。 冒頭の通り、今回はPython 3.8をランタイムに選択します。 f:id:uentseit:20200328114414p:plain 関数の内容は以下の通りです。

import json
import boto3
import base64
import io
from datetime import datetime
import cgi

BUCKET_NAME='xxxx-test-backet-1'
DIRECTORY='uploaded_files/'

s3 = boto3.resource('s3')

def lambda_handler(event, context):
    bucket = s3.Bucket(BUCKET_NAME)
    
    body = base64.b64decode(event['body-json'])
    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('アップロード完了')
    }

multipart/form-dataで複数ファイルをアップロードできるようにしておきます。
ただし、Lambdaのペイロードサイズ上限は6MBとなっているため、事前に要件の確認が必要です。 アップロードされたファイルの読み込みには、cgiモジュールのFieldStorageを利用することにします。
保存先はS3で、Bucket内に'uploaded_files'というフォルダを作ってそこに保存します。
multipart/form-dataで複数アップロードしたいので、保存もファイルごとに行います。
S3への保存は、AWS SDK for Python (Boto3)をimportして利用します。

API Gatewayでインタフェースを作成

トリガーの追加から、API Gatewayを選択します。
f:id:uentseit:20200328114300p:plain API Gatewayのコンソール画面でリソースを設定します。今回はmultipart/form-dataでファイルを送信する為に、アクション→メソッドの作成→POSTを選択してPOSTメソッドを受け付けるようにします。
f:id:uentseit:20200328114532p:plain 統合リクエストにはLambdaを指定しておきます。
f:id:uentseit:20200328114545p:plain

ファイルをアップロードする

Chrome拡張のTalend API Testerを使用してファイルをアップロードします。
今回は試しにエクセルファイルを2つアップロードしてみます。
f:id:uentseit:20200328114634p:plain

すでにアップロード済みのファイルと同じファイル名でアップロードすると上書き保存となります。
以下のようなHTMLをS3上に用意してしまえば、一つのアプリケーションの出来上がりです。

<!doctype html>
<html>
<head>
        <meta charset="utf-8"/>
    </head>
<body>
    <form method="post" action="https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/default/fileUploader" enctype="multipart/form-data">        
        <p>ファイル:<br>
        <input type="file" name="file1" size="30"><br>
        <input type="file" name="file2" size="30"></p>
      
        <p><input type="submit" value="送信する"></p>        
    </form>
</body>
</html>

今回は以上です。

参考リンク

[Python] POSTされたmultipart/form-dataをFieldStorageでパースする - Qiita