AWS API Gateway + Lambda + S3 でバイナリデータを保存する
AWS API Gateway + Lambda + S3 でバイナリデータを保存する:
Qiitaにて他の方も書いている内容になります
今回これを紹介するにあたり画像加工を行うマイクロサービスを作りました
今回はそのサービスの一部のHTTP通信でS3にリソースファイルをアップロードする部分をピックアップしています
デプロイすることでAPIGatewayにリクエストを受けつけるURLが作られます
APIGatewayの設定のバイナリメディアタイプ
※設定を反映するには再度デプロイが必要になります
Qiitaにて他の方も書いている内容になります
今回これを紹介するにあたり画像加工を行うマイクロサービスを作りました
今回はそのサービスの一部のHTTP通信でS3にリソースファイルをアップロードする部分をピックアップしています
作製したマイクロサービス
- HTTP通信でS3(INPUT用バケット)にアップロード
※直接S3にアップロードしても可 -
S3(INPUT)にアップロードされるとトリガーで画像加工Lambdaが起動
※GM on ImageMagick
アップロードするファイルは以下の3つ
- 画像リソース(複数)
- 加工処理レシピファイル
- コールバックファイル(任意)
-
画像加工処理
- リサイズ
- 画像合成
- テキスト合成
- 加工後ファイルに画像合成して更に加工する
- ファイルフォーマット変更
- 完了後にS3(OUTPUT)に保存
※ホスティングを有効にしています - コールバックファイルがある場合は、コールバック先へ加工後のリソースのURLとコールバックファイル内に記載されたレスポンスデータを返します
環境
ServerlessFramework 1.24.1
Node.js 6.10
実装
コード
serverless.yml
# Welcome to Serverless! service: serverless-imagemanager # You can pin your service to only deploy with a specific Serverless version # Check out our docs for more details frameworkVersion: "=1.24.1" # 設定定義 custom: defaultStage: dev # AWSの接続先(aws_credentials) profiles: dev: default prod: default # AWSに反映する設定定義 provider: name: aws runtime: nodejs6.10 region: ap-northeast-1 stage: ${opt:stage, self:custom.defaultStage} profile: ${self:custom.profiles.${self:provider.stage}} memorySize: 1024 timeout: 12 deploymentBucket: deploy.${self:provider.stage}.${self:service} environment: suffix: ${self:provider.stage} service_name: ${self:service} # Lambda function's IAM Role # https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-arns-and-namespaces.html iamRoleStatements: - Effect: 'Allow' Action: # Gives permission to Lambda in a specific region - lambda:InvokeFunction # Gives permission to S3 bucket in a specific - s3:* Resource: - 'arn:aws:s3:::${self:service}.input.dev/*' - 'arn:aws:s3:::${self:service}.input.prod/*' - 'arn:aws:s3:::${self:service}.output.dev/*' - 'arn:aws:s3:::${self:service}.output.prod/*' - '*' # Lambda # you can add packaging information here package: exclude: - .DS_Store - .git/** - .serverless/** - .npmignore - .gitignore # APIリスト # https://serverless.com/framework/docs/providers/aws/guide/events/ # https://serverless.com/framework/docs/providers/aws/guide/serverless.yml/ functions: # HTTPリクエストでS3にアップロード. request_s3_upload_input: handler: src/request_s3_upload_input.handler memorySize: 256 events: - http: path: imagemanager/uploads method: post # CloudFormation resource templates # http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html resources: Resources: # S3 input: Type: AWS::S3::Bucket Properties: BucketName: ${self:service}.input.${self:provider.stage} #DeletionPolicy : Retain output: Type: AWS::S3::Bucket Properties: BucketName: ${self:service}.output.${self:provider.stage} #DeletionPolicy : Retain
src/request_s3_upload_input.handler
'use strict'; const Aws = require('../libs/aws.js'); const Logger = require('../libs/logger.js'); const Resource = require('../model/resource.js'); /** * HTTPプロトコルからアップロード. **/ module.exports.handler = (event, context, callback) => { Logger.info("start request_s3_upload_input."); s3upload(event, context) .then((result) => { callback(null, { statusCode: 200, body: JSON.stringify(result) }); }) .catch((error) => { Logger.error(error); callback(null, { statusCode: 500, body: JSON.stringify(error) }); }); }; function s3upload(event, context) { return new Promise((resolve, reject) => { Logger.info(event); // コンテントタイプの文字列が固定されていなかったので網羅. let contentType = event.headers["Content-Type"]; if (!contentType) { contentType = event.headers["Content-type"]; } if (!contentType) { contentType = event.headers["content-type"]; } if (!contentType) { contentType = event.headers["content-Type"]; } const queryParams = event.queryStringParameters; const key = queryParams.key; const resource = new Resource(); Logger.debug("contentType: " + contentType); Logger.debug(queryParams); let body; switch (contentType) { case "image/jpeg": case "image/gif": case "image/png": // リクエストボディに設定された画像データはBase64エンコードされているので、デコードする body = Buffer.from(event.body, 'base64'); break; default: body = event.body; } const params = { Bucket: resource.getBucketName(), Key : key, Body : body, ContentType: contentType }; Logger.debug(params); resource.savePromiseForS3(params).then(resolve()); }); }
model/resource.js
'use strict'; const Aws = require('../libs/aws.js'); const Logger = require('../libs/logger.js'); module.exports = class Resource { getBucketName() { return process.env.service_name + ".input." + process.env.suffix; } promiseForTakeOverData(object) { return new Promise(function(resolve, reject) { resolve(object); }); } // S3 リソースを参照. loadPromiseForS3(bucketName, key) { const s3 = Aws.s3(); return new Promise(function(resolve, reject) { // S3から読み込み s3.getObject({Bucket: bucketName, Key: key}, function(err, data) { if (err) { Logger.error("Error: loadPromiseForS3." + err.toString()); reject(err); } else { Logger.info("Success: loadPromiseForS3 " + key); resolve(data.Body); } }); }); } // S3 保存. savePromiseForS3(params) { const s3 = Aws.s3(); return new Promise(function(resolve, reject) { // S3に書き込み s3.putObject(params, function(err) { if (err) { Logger.error("Error: savePromiseForS3." + err.toString()); reject(err); } else { Logger.info("Success: savePromiseForS3 " + JSON.stringify(params, null, 2)); resolve(); } }); }); } }
使い方
デプロイすることでAPIGatewayにリクエストを受けつけるURLが作られます- メソッド: POST
- Body部: Base64エンコードしたバイナリソース
- URLクエリパラメータ: 『?key=<<保存ファイル名>>』
注意点
APIGatewayの設定のバイナリメディアタイプ※設定を反映するには再度デプロイが必要になります
コメント
コメントを投稿