AWS Lambdaのimagemagickでリサイズ失敗問題について

結論

先に結論から言うと、Lambdaがデフォルトで提供しているimagemagickライブラリだと特定の画像のリサイズで失敗するっぽいです。 ローカルでnpm install imagemagickしたパッケージを含めてlambdaにzipアップロードして、同じコード同じ画像で試して正常に動いたので間違いないはず。

問題のエラー

以下Lambda functionを「Edit code inline」で入力して、S3によくある普通の画像をアップロードして実行させました。*1

var im = require('imagemagick');
var aws = require('aws-sdk');
var s3 = new aws.S3();
var thumbnailSize = 600;

function resizeImage(context, bucket, key, data) {
    var contentType = data.ContentType;
    var extension = contentType.split('/').pop();
    im.resize({
        srcData: data.Body,
        format: extension,
        timeout: 0,
        width: thumbnailSize
    }, function(err, stdout, stderr) {
        if (err) {
            console.log(err);
            context.done('resize failed: '+key);
            return;
        }

        // avoid endless loop
        if (bucket.match(/-thumbnail$/i)) {
            context.done('no support bucket: '+bucket);
            return;
        }

        var thumbnailBucket = bucket + "-thumbnail";
        var thumbnailKey = 'thumbnail/' + key.split('.')[0] + "-thumbnail." + extension;
        s3.putObject({
            Bucket: thumbnailBucket,
            Key: thumbnailKey,
            Body: new Buffer(stdout, 'binary'),
            ContentType: contentType
        }, function(err, res) {
            if (err) {
                console.log(err);
                context.done('error putting object: '+key);
            } else {
                console.log('create thumbnail: '+thumbnailKey);
                context.done();
            }
        });
    });
}

var createThumbnail = function(event, context) {
    var bucket = event.Records[0].s3.bucket.name;
    var key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));

    s3.getObject({
        Bucket: bucket,
        Key: key,
        IfMatch: event.Records[0].s3.object.eTag
    }, function(err, data) {
        if (err) {
            console.log(err);
            context.done('error getting object: '+key);
            return;
        }

        resizeImage(context, bucket, key, data);
    });
};

exports.handler = function(event, context) {
    createThumbnail(event, context);
};

すると以下のログがCloud Watchに出力されました。

{ [Error: Command failed: ] timedOut: false, killed: true, code: null, signal: 'SIGKILL' }

メモリを上げ下げしたり、画像の解像度・ファイルサイズ、画像の形式・メタ情報まで色々変更して試してみても改善されず。 最終手段で、imagemagickを含めてアップして実行するとすんなり動きました。意味がわからない。

まとめ

素直にnpmで最新版(安定版)を準備してまとめてLambdaにzipアップロードが一番良いと思います。 ちなみにzipアップロードですが、以下のようなshellでzipを作ってます。そのままフォルダを圧縮するとエラーになるので気をつけてください。

#!/bin/sh
zip -r lambda index.js node_modules

*1:こちらを参考にさせてもらいました。

qiita.com

photo credit: Café au lait via photopin (license)