Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

やりたいこと

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

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away