やりたいこと
- Amazon S3 に貯めてきた大量の画像を、すべてリサイズ&加工したい.
- 正方形画像を同比率のままリサイズし、左右に帯を付けたい.
- でも、加工処理のためのサーバをたてたり、画像ファイルを出し入れしたくない.
- あっ、AWS Lambda でいけんじゃね.
課題
Lambda Function はイベント駆動での処理を想定した仕組みなので、「S3 に新しく追加/更新した画像を、その場で変換」にはうまくフィットするのだが、「既に S3 に入っている大量の画像を逐次変換」にはフィットしないように思える.
解決策
Invoke API をアクセスしてやれば良い.
サンプル
画像加工処理
AWS Lambda ウォークスルー 2: AWS CLI を使用して Amazon S3 イベントを処理する (Node.js) - AWS Lambda のサンプルを参考に、画像の取得元と、加工処理、保存先をそれぞれ以下のように書き換えた.
// dependencies
var async = require('async');
var AWS = require('aws-sdk');
var gm = require('gm').subClass({ imageMagick: true }); // Enable ImageMagick integration.
var util = require('util');
var path = require('path');
// get reference to S3 client
var s3 = new AWS.S3();
exports.handler = function(event, context) {
// Read options from the event.
console.log("Reading options from event:\n", util.inspect(event, {depth: 5}));
var srcBucket = event.Records[0].s3.bucket.name;
// Object key may have spaces or unicode non-ASCII characters.
var srcKey =
decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " "));
// 格納先は、元画像と同じ場所に.
var dstBucket = srcBucket;
var dstKey = path.dirname(event.Records[0].s3.object.key) + "/resized.jpg";
// Infer the image type.
var typeMatch = srcKey.match(/\.([^.]*)$/);
if (!typeMatch) {
console.error('unable to infer image type for key ' + srcKey);
return;
}
var imageType = typeMatch[1];
if (imageType != "jpg") {
console.log('skipping non-image ' + srcKey);
return;
}
// Download the image from S3, transform, and upload to a different S3 bucket.
async.waterfall([
function download(next) {
// Download the image from S3 into a buffer.
s3.getObject({
Bucket: srcBucket,
Key: srcKey
},
next);
},
function transform(response, next) {
gm(response.Body).size(function(err, size) {
var width = 640;
var height = 480;
// Transform the image buffer in memory.
// 比率をそのままにリサイズし、左右に黒帯をつける.
this.background('#000000')
.resize(width, height)
.gravity("Center")
.extent(width, height)
.toBuffer(imageType, function(err, buffer) {
if (err) {
next(err);
} else {
next(null, response.ContentType, buffer);
}
});
});
},
function upload(contentType, data, next) {
// Stream the transformed image to a different S3 bucket.
s3.putObject({
Bucket: dstBucket,
Key: dstKey,
Body: data,
ContentType: contentType
},
next);
}
], function (err) {
if (err) {
console.error(
'Unable to resize ' + srcBucket + '/' + srcKey +
' and upload to ' + dstBucket + '/' + dstKey +
' due to an error: ' + err
);
} else {
console.log(
'Successfully resized ' + srcBucket + '/' + srcKey +
' and uploaded to ' + dstBucket + '/' + dstKey
);
}
context.done();
}
);
};
起動
S3 に保存されている画像に対して、ステップ 2.3: デプロイパッケージをアップロードしてテストする(Amazon S3 | Node.js) - AWS Lambda にあるようなイベントデータ JSON を組み立てながら順次 invoke をコールしてやる.
ハマったこと
Important
フォルダーそのものではなく、フォルダーの内容を zip 圧縮します。
って書いてあるのに、フォルダそのまま zip してアップロードしてしまった件.
こういう利用方法はアリなのか
よくある質問 - AWS Lambda | AWS には以下のようにあるので、通常の利用範囲内のように思える.
Q: アプリケーションで AWS Lambda 関数を直接トリガーするにはどうすればよいですか?
AWS Lambda の「invoke」API でカスタムイベントを使用して、Lambda 関数を呼び出すことができます。関数の所有者または所有者が許可を与えた他の AWS アカウントのみ関数を呼び出すことができます。詳しくは、Lambda Developers Guide をご覧ください。