AWS Lambda で画像のサムネイルを作る

What for?

  • 基本の素振りはできたので、少しは役に立つものを作る
  • S3にアップロードされた画像のサムネイルをつくる

Prepare

var im = require('imagemagick');

im.readMetadata('hoge.jpg', function(err, metadata){
      if (err) throw err;
        console.log('Shot at '+metadata.exif.dateTimeOriginal);
})
  • convertでのリサイズ
    • S3のデータにどうやってconvertかけよう?
      • バイナリデータ拾ってきて流し込みたいな
var im = require('imagemagick');

im.convert(['hoge.jpg', '-resize', '25x120', 'hoge-small.jpg'],
        function(err, stdout){
              if (err) throw err;
                console.log('stdout:', stdout);
        });
var im = require('imagemagick');
var fs = require('fs');

im.resize({
  srcData: fs.readFileSync('hoge.jpg', 'binary'),
  width:   256
}, function(err, stdout, stderr){
  if (err) throw err
  fs.writeFileSync('hoge-resized.jpg', stdout, 'binary');
  console.log('resized kittens.jpg to fit within 256x256px')
});
  • S3のObject取ってくる練習

    • 準備で対象S3にfull accessできるIAM role作っとく
    • 実行するときは、Role指定で実行 $ AWS_PROFILE=rw-s3 node simpleS3.js
  • BacketList取ってくるだけ

var AWS = require('aws-sdk');

s3 = new AWS.S3();

s3.listBuckets(function(err, data) {
  if (err) { console.log("Error:", err); }
  else {
    for (var index in data.Buckets) {
      var bucket = data.Buckets[index];
      console.log("Bucket: ", bucket.Name, ' : ', bucket.CreationDate);
    }
  }
});
  • S3からファイルをローカルに保存
    • readstreamでデータ取ってきて、fsのwritestreamでputする
var AWS = require('aws-sdk');
var fs = require('fs');

var s3 = new AWS.S3();

var params = {Bucket: 'for-lambda-test', Key: 'hoge.jpg'};
var file = fs.createWriteStream('output.jpg');
s3.getObject(params).createReadStream().pipe(file);

Code

  • コードの大きな流れ
    • S3にファイルをput -> Lambda関数が呼ばれる -> eventからS3 objectを取得 -> w100_ のprefixの有無の確認 -> convertする -> 同じバケットにサムネイルを保存する
var AWS = require('aws-sdk');
var fs = require('fs');
var im = require('imagemagick');
var s3 = new AWS.S3();

exports.handler = function (event, context) {
  var bucket = event.Records[0].s3.bucket.name;
  var key    = event.Records[0].s3.object.key;
  var params = {
    Bucket: bucket,
    Key   : key
  };

  console.log(params);
  // For stop infinity loop
  if (key.substr(0,5) === 'w100_') {
    return;
  }

  s3.getObject(params, function (err, data) {
    if (err) {
      context.done('error on get obj', err);
    }
    console.log(data);

    im.resize({
      srcData: data.Body,
      width: 100
    }, function (err, stdout, stderr) {
      if (err) {
        context.done('error on resizing', err);
      }
      s3.putObject({
        Bucket: bucket,
        Key   : 'w100_' + key,
        Body  : new Buffer(stdout, 'binary')
      }, function (err, res) {
        if (err) {
          context.done('error on put obj', err);
        }
        context.done();
      });
    });
  });
};
  • 初回は、 w100_ のprefixの有無の確認 の記述を書き忘れて、無限ループに突入していた
    • ファイルが数百個できて悲惨だった...orz
  // For stop infinity loop
  if (key.substr(0,5) === 'w100_') {
    return;
  }

TODO

  • Node.js のoutputやらエラーハンドリング周りがわかってない
  • Node.js の writeStream やら readStream やらがしっくりきてない
  • 不安だからテスト書きながら進めたいけど、Node.jsのテスト手法やいかに
  • node-imagemagick の最終更新が Jul 24, 2014 なので違う方法のほうが良さそう