目的
コンテンツをサーバー上にアップロード・ダウンロードして配信するというユースケースがある。コンテンツを保持しておくサービスとして、AWSにS3というサービスがあるのでそれを利用する。
環境
- AWS Lambda Python 3.9
Lambdaコード
# -- 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
S3_BUCKET_NAMEはGlobalで一意である必要があった気がするので変える必要があります。
S3バケットの作成
環境変数で設定したS3_BUCKET_NAMEのバケット名でS3にバケットを作成する。
IAM
LambdaのExecution RoleにS3へのPUTとGETを許すように変更する。
Lambdaの作成段階で割当たっているロールに、以下のようなポリシーをアタッチする。
{
"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をアップロードする。
export url="<出力されたPUTのPresignedURL>"
curl -X PUT --upload-file sample.jpg $url
GETはブラウザに取得したPresignedURLを直打ちすることでダウンロードもしくは表示されることが確認できる。
詰まったところ
さまざまなサイトで、
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の実行権限がタスクロールに割り当てられているかを確認する。
参考