S3にアップした画像をLambdaでリサイズして別バケット(bucket)に保存するNode.jsのサンプルコード

とある案件でS3にアップされるオリジナル画像をリサイズで別バケットに保存したい要望があり先人の知恵と伝家の宝刀コピペを駆使して組んでみました。

処理の流れ
[オリジナル.jpg]→PUT→S3bucket→イベントキック→Lambda(リサイズbyNode.js)→S3別bucket保存

この処理の流れですが、案外ナレッジがネットにコロガッテいない。ナレッジの多くは同一バケットにリサイズした画像を保存するものばかりで私の要件には合わないのです
例えば

AWS Lambdaを使ってS3にアップロードされた画像をリサイズする - Qiita
# Lambdaファンクションを作る ## ブループリント AWSの管理画面からLambdaに行くと、ブループリントという画面が出て 用途に応じたLambdaファンクションのテンプレートが選べます。 今回は「image-proce...

どうしたものかとGoogle検索で様々なキーワードで検索をしていると

LambdaでS3にアップロードされが画像をサムネイルにしてみた。 – ただいま名刺を切らしておりまして

このコードなら理解といいますか加工すれば、なんか出来そうな気がしてきました。
Lambdaファンクション用のNode.jsコードです

var im = require('imagemagick');
var aws = require('aws-sdk');
var s3 = new aws.S3({ apiVersion: '2006-03-01' });

exports.handler = function(event, context) {
    var bucket = event.Records[0].s3.bucket.name;
    var key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
    var params = {
        Bucket: bucket,
        Key: key
    };
    s3.getObject(params, function(err, data) {
        if (err) {
            console.log(err);
            var message = "Error getting object " + key + " from bucket " + bucket +
                ". Make sure they exist and your bucket is in the same region as this function.";
            console.log(message);
            context.fail(message);
        } else {
            var contentType = data.ContentType;
            var extension = contentType.split('/').pop();
            
            var filename = 'images/' + (key.match(/^origin\/(.+)$/) ? RegExp.$1 : key);

            im.resize({
                srcData: data.Body,
                format: extension,
                width: 480 // リサイズする画像サイズを指定
            }, function(err, stdout, stderr) {
                if(err) {
                    context.fail(err);
                    return;
                }
                s3.putObject({
                    Bucket: bucket,
                    Key : filename,
                    Body: new Buffer(stdout, 'binary'),
                    ACL : 'public-read',
                    ContentType: contentType
                }, function(err, res) {
                    if (err) {
                        context.fail(err);
                    } else {
                        context.succeed();
                    }
                });
            });
        }
    });
};

このコードだと
S3-UPBucket(オリジナル画像がアップされるバケット)
└images/ (オリジナルをリサイズしたらimages/以下に保存される)

私の要件は
S3┬UPBucket(オリジナル画像がアップされるバケット)
└ReSizeBucket(リサイズした画像は別のバケットに保存したい)

なので

                s3.putObject({
                    Bucket: bucket,←このbucketが'ReSizeBucket'になれば良い
                    Key : filename,
                    Body: new Buffer(stdout, 'binary'),
                    ACL : 'public-read',
                    ContentType: contentType

加えて

            var filename = 'images/' + (key.match(/^origin\/(.+)$/) ? RegExp.$1 : key);
       ↓
            var filename = 'バケット以下のフォルダを指定する' + (key.match(/^origin\/(.+)$/) ? RegExp.$1 : key);

さすれば、私の要件が叶えられるはず
でNode.jsとかコードを書けないので「サルでもわかる」系を見てみる。
で完成したのが以下のコードです

var im = require('imagemagick');
var aws = require('aws-sdk');
var s3 = new aws.S3({ apiVersion: '2006-03-01' });
var outbucket = "ReSizeBucketName"; // リサイズ後の画像を保存するバケットを指定

exports.handler = function(event, context) {
    var bucket = event.Records[0].s3.bucket.name;
    var key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
    var params = {
        Bucket: bucket,
        Key: key
    };
    s3.getObject(params, function(err, data) {
        if (err) {
            console.log(err);
            var message = "Error getting object " + key + " from bucket " + bucket +
                ". Make sure they exist and your bucket is in the same region as this function.";
            console.log(message);
            context.fail(message);
        } else {
            var contentType = data.ContentType;
            var extension = contentType.split('/').pop();
            //保存バケット以下のパス情報を指定
            var filename = 'Resize-Image/○○○○/○○○/' + (key.match(/^origin\/(.+)$/) ? RegExp.$1 : key);

            im.resize({
                srcData: data.Body,
                format: extension,
                width: 320 // リサイズする画像サイズを指定
            }, function(err, stdout, stderr) {
                if(err) {
                    context.fail(err);
                    return;
                }
                s3.putObject({
                    Bucket: outbucket, // 変数outbucketをPUTするbucketに指定する
                    Key : filename,
                    Body: new Buffer(stdout, 'binary'),
                    ACL : 'public-read',
                    ContentType: contentType
                }, function(err, res) {
                    if (err) {
                        context.fail(err);
                    } else {
                        context.succeed();
                    }
                });
            });
        }
    });
};

でLambdaに早速コードを入れてテスト実行したら、何事もなく正常終了しました。おぉーーーーー

オリジナル画像をアップするS3のバケットを選択して「Events」で*******.jpgがPutされたらリサイズ処理をするLambdaがキックされるように設定する。

次にバケットに適当な画像.jpgをアップロードしてみます。・・・・・・

するとvar outbucket = “ReSizeBucketName”;で指定したバケットにリサイズされた画像が生成されている。。。

何が大変だったか サンプルを探す時間です
そして無限ループに注意と言われても やっちゃうんだよなぁ 数百枚分のLambdaを実行してしまった。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする