AWSネイティブな実装で、ブラウザからS3へアップロード。api gateway -> lambda(python)でs3の署名付きURL取得 ->htmlから直接s3へアップロード
lambdaのペイロードが6MBしかないので、lambda経由のアップロードは無理だった。
セキュリティ最優先のためだろうけど、設定する場所が多すぎ!
参考URL
https://qiita.com/jun0o0/items/41fbcc28dfcafa6052cd
1, S3のバケット作成
1-1, 「パブリックアクセスをすべて ブロック」をオフ
1-2, バケットポリシーを記述(Read許可とIP制限)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::バケット名/*", "Condition": { "IpAddress": { "aws:SourceIp": "11.22.33.44/32" } } } ] } |
1-3, Cross-Origin Resource Sharing (CORS)を設定。アップロード元を、どのオリジンからでもOKにする
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "PUT", "POST", "DELETE" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [] } ] |
1-4, プロパティタグの一番下に有る「静的ウェブサイトホスティング」を有効化。インデックスドキュメントをindex.html
2, lambda(python)関数で、s3の署名付きURLを生成。AmazonS3FullAccessポリシーで権限を付与しておく
https://qiita.com/jun0o0/items/41fbcc28dfcafa6052cd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import boto3 from botocore.client import Config import json import uuid def lambda_handler(event, context): PUT_BUCKET = 'バケット名' #S3バケット名 UUID = str(uuid.uuid4()) #一意なランダム値を生成 PUT_KEY = 'uploads/' + UUID #S3バケット上のファイルの保存先 BORROW_TIME = 100 #署名付き URLの制限時間 s3 = boto3.client('s3', region_name='ap-northeast-1', config=Config(signature_version='s3v4')) #バケット上に空ファイルを作成 s3.put_object( Bucket=PUT_BUCKET, Key=PUT_KEY ) #空ファイルへのアップロードを可能にする署名付きURLを生成 put_url = s3.generate_presigned_url( ClientMethod = 'put_object', Params = {'Bucket' : PUT_BUCKET, 'Key' : PUT_KEY}, ExpiresIn = BORROW_TIME, HttpMethod = 'PUT') #署名付きURLとuuidを返す return { 'statusCode': 200, 'isBase64Encoded': False, 'headers': { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'OPTIONS,GET' }, 'body': json.dumps( { 'put_url': put_url, 'uuid': UUID } ) } |
3, lambdaのトリガーからapi gateway作成(REST API)
3-1, IP制限しているので、SecurityはOpen
3-2, /Lambda名のGETメソッドを作成。2で作ったlambdaを割り当てる
3-3, CORS対応。メソッドレスポンスのレスポンスヘッダーに、以下の3つを追加
Access-Control-Allow-Headers
Access-Control-Allow-Methods
Access-Control-Allow-Origin
3-4, 統合レスポンスのヘッダーのマッピングを編集
Access-Control-Allow-Headers ’Content-Type,X-Amz-Date,Authorization,X-Requested-With,X-Requested-By,X-Api-Key’
Access-Control-Allow-Methods ’GET,POST’
Access-Control-Allow-Origin ’*’
3-3, ステージはdefaultでデプロイして、URLを取得
https://乱数.execute-api.ap-northeast-1.amazonaws.com/default/Lambdaの関数名
4, ブラウザからアップロード操作するindex.htmlをs3に配置。静的ホスティングしているからs3のurlから使える。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <input type='file' id='file-input'> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> async function uploadFile(event) { //ファイルを取得する const upload_file = event.target.files[0]; //APIエンドポイントにGETリクエストを送る const res_signed_url = await axios.get('api gatewayのURL'); //レスポンスから署名付きURLを取り出す const signed_url = JSON.parse(res_signed_url.data.body).put_url; //署名付きURLにファイルをPUTする await axios.put( signed_url, upload_file, { headers: { 'Content-Type': upload_file.type } } ); }; const input = document.getElementById('file-input'); input.addEventListener('change', (event) => uploadFile(event)); </script> </body> </html> |