44
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Amazon S3 にある大量の既存画像を AWS Lambda で一気に変換する

Posted at

やりたいこと

  • Amazon S3 に貯めてきた大量の画像を、すべてリサイズ&加工したい.
    • 正方形画像を同比率のままリサイズし、左右に帯を付けたい.
  • でも、加工処理のためのサーバをたてたり、画像ファイルを出し入れしたくない.
  • あっ、AWS Lambda でいけんじゃね.

課題

Lambda Function はイベント駆動での処理を想定した仕組みなので、「S3 に新しく追加/更新した画像を、その場で変換」にはうまくフィットするのだが、「既に S3 に入っている大量の画像を逐次変換」にはフィットしないように思える.

解決策

Invoke API をアクセスしてやれば良い.

サンプル

画像加工処理

AWS Lambda ウォークスルー 2: AWS CLI を使用して Amazon S3 イベントを処理する (Node.js) - AWS Lambda のサンプルを参考に、画像の取得元と、加工処理、保存先をそれぞれ以下のように書き換えた.

ResizeImage.js
// 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 をご覧ください。

参考

44
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?