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=<<保存ファイル名>>』