14
12

More than 5 years have passed since last update.

Lambdaで画像のサムネイルを作る [Stream yields empty buffer対策済]

Last updated at Posted at 2017-02-25

S3に画像をアップロードしたときにその画像のサムネイルを自動生成する…という類のものです。はじめは、本家のチュートリアルを参考にしてやってみたんですが、[Stream yields empty buffer]というエラーが出て行き詰まり、解決を探しても見つからなかったので、結局自作しました。

ちなみに、AWS Lambdaでサムネイルを作る本家チュートリアルはこちら

[Stream yields empty buffer]を調べて行き着いたのがこれら
- AWS Developer Forums: Stream yields empty buffer error when ...
- node.js - GraphicsMagick: toBuffer() Stream yields empty buffer
- node.js - gm Error in aws lambda: string yields empty buffer
- node.js - Got Error: Stream yields empty buffer when resizing images

で、結局以下のように自作した。

index.js
var AWS = require('aws-sdk');
var gm = require('gm').subClass({ imageMagick: true });
var util = require('util');
var s3 = new AWS.S3();

exports.handler = function(event, context, callback) {

  var bucket = event.Records[0].s3.bucket.name; // 変換元画像のバケット名
  var key = event.Records[0].s3.object.key;     // 変換元画像のキー名
  var thumbnailBucket = bucket + "-thumbnail";  // 変換後画像のバケット名(ここでは末尾にthumbnailを付け足すことにしてある)
  var thumbnailKey = key;                       // 変換後画像のキー名(ここでは変換前と同じにしてある)

  // 拡張子を抽出
  var typeMatch = key.match(/\.([^.]*)$/);
  if (!typeMatch) {
    callback("Could not determine the image type.");
    return;
  }

  // 拡張子がjpg, jpeg, pngのものだけ許可する
  var imageType = typeMatch[1];
  if (imageType != "jpg" && imageType != "jpeg" && imageType != "png") {
    callback('Unsupported image type: ${imageType}');
    return;
  }

  // S3の画像にアクセス
  s3.getObject({Bucket: bucket, Key: key}, function(err, data){
    if (err) { context.done('error getting object', err); }

    // 画像処理 (http://qiita.com/komakomako/items/a33ff4e610e378d986ff と同じ)
    gm(data.Body)
    .resize(null, '350')
    .borderColor('black')
    .border('245', '245')
    .gravity('Center')
    .crop('490', '350')
    .stream(function(err,stdout,stderr){ // ストリームで出力する
      if(err){
        console.log("gm process error");
        console.log(err,stdout,stderr);
        context.fail(err);
      }
      var chunks = []; // ストリームのチャンクを溜め込むための配列
      stdout.on('data',function(chunk){
        console.log('pushed');
        chunks.push(chunk); // チャンクを溜め込む
      });
      stdout.on('end',function(){
        console.log('end');
        var buffer = Buffer.concat(chunks); // 最後まで溜め込んだら結合してバッファに書き出す
        var params = {
          Bucket: thumbnailBucket,
          Key: thumbnailKey,
          ContentType: imageType,
          Body: buffer
        };
        s3.putObject(params, function(err, data) { // S3に書き出す
          if(err){
            console.log("gm upload error");
            context.fail(err);
          }
          context.succeed({
            "error":false
          });
        });
      });

      stderr.on('data',function(data){
        console.log('stderr data: ' +  data);
      });
    });
  });
};

Lambdaへのデプロイの仕方

外部ライブラリgmを使用しているので、上記のindex.jsと同じディレクトリで以下のコマンドを打ってこれらをインストール

npm install gm

すると、以下のようなディレクトリ構造になる。

- hogehoge/
    - index.js
    - node_modules/

次にindex.jsと同じディレクトリで以下のコマンドを打ってzipを作る。

zip a.zip *

すると、以下のようなディレクトリ構造になる。

- hogehoge/
    - a.zip
    - index.js
    - node_modules/

このa.zipをLambdaのブラウザコンソールからアップロードすればおk

14
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
12