質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.05%

[Javascript] AWS Lambda内で処理している画像処理に関して

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,042

score 263

はじめに

S3のある一つのバケットに保存している全ての画像のサムネイルを生成したいと思っています

問題

AWS Lambdaを使用して、下記のような関数を作成しました。

var async = require('async');
var AWS = require('aws-sdk');
var gm = require('gm').subClass({ imageMagick: true }); // Enable ImageMagick integration.
var util = require('util');
var path = require('path');

// get reference to S3 client 
var s3 = new AWS.S3();

exports.handler = function(event, context) {

     var bucket = "develop-hoge-files";
     var items = [];
     var params = {
       Bucket: bucket,
       Prefix: "images/"
     };

     //S3対象のバケット内の画像を全て取得
     s3.listObjects(params, function(err, data) {
       if (err) 
         console.log(err, err.stack); // an error occurred
       else 
         for (var i = 0; i < data.Contents.length; i++) {
             if (data.Contents[i].Key.match(".jpeg") && data.Contents[i].Key.match("images/")) {
                 //コールバックで返ってきたデータを使用して、画像のキーを取得し、画像のリサイズとその画像を任意のバケットにアップロードする関数を呼び出す
                 imageResizeUpload(data.Contents[i].Key);
             }
         }
     });

};

function imageResizeUpload(key) {
        async.waterfall([
        function download(next) {
            // Download the image from S3 into a buffer.
            s3.getObject({
                    Bucket: "develop-hoge-files",
                    Key: key
                },
                next);
            },
        function transform(response, next) {
            gm(response.Body).size(function(err, size) {
                var width  = 640;
                var height = 480;

                // Transform the image buffer in memory.
                this.resize(width, height)
                    .toBuffer("jpeg", function(err, buffer) {
                        if (err) {
                            next(err);
                        } else {
                            next(null, response.ContentType, buffer);
                        }
                });
            });
        },
        function upload(contentType, data, next) {
            // Stream the transformed image to a different S3 bucket.
            s3.putObject({
                    Bucket: "develop-hoge-files",
                    Key: key.replace("images","test-thumbnail-images"),
                    Body: data,
                    ContentType: contentType
                },
                next);
            }
        ], function (err) {
            if (err) {
                console.log("error");
            } else {
                console.log("success");
            }
          }
      );
}

バケット内に画像が200枚ほど存在していまして、それを一つ一つ変換し任意のバケットにアップロードしていきたいのですが、そこの処理をどのように工夫すればいまいちピンとこず。
画像のリサイズから変換に時間を要するので、それが終わった瞬間に次の処理に移るといった書き方が知りたいです。

具体的に変換を行なっているコードは以下の部分です。 imageResizeUpload 関数で、リサイズとアップロードを行なっています。

for (var i = 0; i < data.Contents.length; i++) {
  if (data.Contents[i].Key.match(".jpeg") && data.Contents[i].Key.match("images/")) {
   //コールバックで返ってきたデータを使用して、画像のキーを取得し、画像のリサイズとその画像を任意のバケットにアップロードする関数を呼び出す
   imageResizeUpload(data.Contents[i].Key);
  }
}

どなたかアドバイスできる方いらっしゃいましたら、お願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

async使えてるならもう一息じゃないですか、
JavaScriptの関数は普通の値のように、「変数やプロパティに代入できる」、「関数の引数に渡せる」、「関数の戻り値として受ける」という特性があります。

これを利用して、「引数を覚えるだけの関数」で包み、実行用の関数を返す関数を実装しましょう。

function imageResizeUpload(key) {
  return function (next) {
    // 旧imageResizeUploadとほぼ同じ挙動だが、最後にnext()と実行する作りに変更
  }
}

// 関数の箱を入れる箱
var imageFns = [];

for (var i = 0; i < data.Contents.length; i++) {
  if (data.Contents[i].Key.match(".jpeg") && data.Contents[i].Key.match("images/")) {
    // こうすることでimagesの中にkey引数を束縛した関数が次々と飛び込んでいく
    var imageFn = imageResizeUpload(data.Contents[i].Key);
    imageFns.push(image);
  }
}

console.log(imageFns.length); // 多分200が表示される

async.waterfall(imageFns, function (err) {
  if (err) console.log(err);
  console.log('完了');
});

まぁ、Lambdaの性質やS3の機能を考える限り、
S3はファイルが保存されたタイミングでLambda関数を実行するというイベント通知が可能ですし、
Lambdaは引数を受け取って実行出来るので、S3に画像が保存される度に1個の画像ファイルのサムネイルを作りますという作りにした方が100倍楽だと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/10 11:56

    ご返信遅れましてすみません。解説大変わかりやすかったです、ありがとうございます!上記のコードでうまく変換することができました。「画像保存のタイミングでサムネイルを生成する」については、対応済みで既存のバケットで対応してなかったものがあったような状況でしたm(_ _)m

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.05%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る