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

  • 31
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

やりたいこと

  • 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 をご覧ください。

参考