LoginSignup
13
21

More than 5 years have passed since last update.

AWS lambdaを使ってS3へのアップをトリガーにサムネイルを作る

Last updated at Posted at 2018-04-18

こちらの記事を参考にさせてもらいました。
AWS lambdaを利用してS3の画像アップロードをトリガーにサムネイル画像を自動生成する

流れ

  1. S3のバケットを作る(オリジナル用とサムネイル用の二つ)
  2. サムネイル作成用のlambdaの関数をつくる
  3. 関数の動作テスト
  4. トリガーの有効化
  5. 実際に動かす

1. S3のバケットを作る(オリジナル用とサムネイル用の二つ)

オリジナルの画像を保存するバケットとサムネイルを保存するバケットを用意します。
※一つのバケットにまとめるとプレフィックスを適切に設定しないと無限ループが起きるようです。
今回、サムネイル用のバケットは[オリジナルのバケット名]-resizedという名前の前提で話を進めます。
そのように二つのバケットを作成してください。

2. サムネイル作成用のlambdaの関数をつくる

image.png

右上の「関数の作成」から

image.png

「設計図」を選択

image.png

「s3-get-object」を選択

image.png

「名前」と「ロール名」はなんでもOKです。
「ロール」は「テンプレートから新しいロールを作成」を選択
「ポリシーテンプレート」は「S3オブジェクトの読み取り専用アクセス権限」

image.png

「バケット」には先ほど作成したオリジナルのバケットを選択。
「イベントタイプ」にはオブジェクトの作成(すべて)を選択。
(ここをPUTにすると、プログラムから画像をアップロードした時に関数が動きませんでした。ここで小一時間詰まった...)

image.png

今は一旦チェックは入れない。

image.png

ここは編集不可なのでそのまま

image.png

「関数の作成」

image.png

すると関数ができる。

このあと関数を書いていくのですが、本家のチュートリアル通りにやっても[Stream yields empty buffer]というエラーが出てうまくいきません。
本家チュートリアル

同じエラーに遭遇した方のこちらの記事を参考にしました。ありがとうございます。
Lambdaで画像のサムネイルを作る [Stream yields empty buffer対策済]

この後、サムネイル作成用の関数をローカルで作って、それをlambdaにアップロードします。

ローカルにて

$ mkdir hoge
$ cd hoge
$ npm install gm

適当にディレクトリを作って、gmをインストールします。(GraphicsMagickというライブラリ)

そしてindex.jsを作って以下のように書きます。先ほどの参考サイトとほぼ同じです。
上から7行目のkeyを作るところの処理が公式と違ったので、公式に合わせています。(decodeURIComponentを使っている。)
また、正方形のサムネイルを作りたかったのでcropの引数を200x200に変えています。

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 = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));     // 変換元画像のキー名
  var thumbnailBucket = bucket + "-resized";  // 変換後画像のバケット名(ここでは末尾にresizedを付け足すことにしてある)
  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('200', '200')
    .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);
      });
    });
  });
};

image.png

index.jsnode_moduleを圧縮。

image.png

lambdaのコンソールにて、今作ったzipをアップロードする。
ここまでで関数の作成が完了。
次はテストイベントの設定。

3. 関数の動作テスト

image.png

この画像だともう設定されてしまっていますが、画面上のほうにテストを設定するところがある。

image.png

このように設定

スクリーンショット 2018-04-18 19.06.04.png

key・・・ここに設定した名前の画像ファイルをこのあとオリジナル用のバケットに入れます。
arn・・・オリジナル用のバケット名に変更する
name・・・同じくオリジナル用のバケット名に変更する
awsRegion・・・S3のバケットが属するリージョンと同じリージョン。ほとんどの人がap-northeast-1かと。

image.png

オリジナルのバケットにHappyFace.jpgというファイルをあげておく。

image.png

画面右上のテストボタンを押すと先ほど書いた関数が動きます。

image.png

"errorMessage": "2018-04-18T10:13:51.573Z 2ed4597f-42f1-11e8-a4de-a37fff25ed59 Task timed out after 3.00 seconds"

私の場合エラーになりました。タイムアウトです。

image.png

タイムアウトの時間はデフォルトで3秒なので、ここを15秒にしてみました。
それでもエラーになる場合はそのすぐ上にあるメモリの割当量を増やしてみてください。

image.png

無事、処理が成功しました。

image.png

サムネイル用のバケットをみてみるときちんと画像が入ってます。
テストは成功しました。
ただ今の状態だと画像をアップロードしたときにこの関数は走ってくれません。なぜならこの関数を作った際、「トリガーを有効にする」のチェックを外したからです。
ですので次にトリガーの有効化をします。

4. トリガーの有効化

image.png

トリガーの有効化の場所を発見するのに地味に時間がかかりました...
lambdaコンソールの上の方の「S3」を選択し、スライダーを押して有効化します。そして忘れずに保存。

5.実際に動かす

image.png

S3に何か画像をアップロードしてみてください。

image.png

サムネイル用のバケットにサムネイルがきちんと入りました!!

前回の記事のサムネイル作成のところはクリアできました。
S3に直接画像をアップロードする(Ruby on Rails)

13
21
1

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
13
21