hamayuzinの日記

ITベンチャーに新卒入社し、エンジニアとかデータサイエンティスト、とかやってます。あの時 あれやってたな的な備忘録にできれば。

【aws/api gateway/lambda/s3】 api gateway ⇔ lambda ⇔ s3 で、動的に画像のresize lambdaでのresize編

目次

  1. 全体像編
  2. lambdaでのresize編 いまここ
  3. api gateway 編 
  4. railsから叩いてみる編
  5. S3で404redirect 番外編

lambdaの実装

speeeの方を参考にしつつ

  • S3から画像を取得 image magicで画像をresize resizeした画像をs3にアップロード *画像のbinaryデータを返す

を実装する

resizeパラメーターは 全体像編でも書いたが * endpoin_url/300/s3_info * endpoin_url/300x300/s3_info * endpoin_url/crop200x200+370+370/s3_info

reizeするための api URLの例は endpoin_url/crop200x200+370+370/bucket_name/image/1/hogehoge.png

こんな感じでくるので、抜き出す必要あり

実際のコードがこちら

lambdaとうことで、node.js

'use strict';

const im = require('imagemagick');
const aws = require('aws-sdk');
const fs = require('fs');
const s3 = new aws.S3({ apiVersion: '2006-03-01' });

const TMP_INPUT_FILE_PATH = '/tmp/inputFile';
const CONVERTED_TMPFILE_NAME = '/tmp/converted_tmpfile';

exports.handler = (event, context, callback) => {
    
    var key_array = event.parameter.split('/');
    key_array.splice(0,1);
    key_array.splice(-2,1);
    
    // 元画像のパスを取得
    const key = key_array.join('/');
    const bucket = event.parameter.split('/')[0] ;
    const params = {
        Bucket: bucket,
        Key: key,
    };

    // 元画像のファイル形式を取得
    var reg=/(.*)(?:\.([^.]+$))/;
    var file_type_match = key.match(reg);
    event.outPutName = event.parameter.split('/').slice(1).join('/');
    event.outPutFileExtension = file_type_match[2];

    // resize用のパラメーター取得
    var reisze_params = event.parameter.split('/')[event.parameter.split('/').length - 2].split('-');
    var size = '';
    var crop = '';
    var gravity = '';
    var match = '';

    for (var i = 0; i < reisze_params.length; i++) {
        if (!size) {
            match = reisze_params[i].match(/^\d*%?x?\d*%?[\^!<>@]?$/i);
            if (match) {
              size = match[0];
              continue;
            }
        }
        if (!crop) {
            match = reisze_params[i].match(/^crop(\d+x\d+\+\d+\+\d+)$/i);
            if (match) {
              crop = match[1];
              continue;
            }
        }
        if (!gravity) {
            match = reisze_params[i].match(/^(NorthWest|North|NorthEast|West|Center|East|SouthWest|South|SouthEast)$/i);
            if (match) {
              gravity = match[0];
              continue;
            }
        }
    }
    console.log(size);
    console.log(crop);
    console.log(gravity);

    var convertParams = [];
    convertParams.push(TMP_INPUT_FILE_PATH);
    convertParams.push('-auto-orient');
    if (size) {
        convertParams.push('-resize');
        convertParams.push(size);
    }
    if (gravity) {
        convertParams.push('-gravity');
        convertParams.push(gravity);
    }
    if (crop) {
        convertParams.push('-crop');
        convertParams.push(crop);
        convertParams.push("+repage");
    }
    convertParams.push(CONVERTED_TMPFILE_NAME);
    console.log(convertParams)
    
    s3.getObject(params, (err, data) => {
        if (err) {
            console.log(err);
            const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
            console.log(message);
            callback(message);
        } else {
            fs.writeFile(TMP_INPUT_FILE_PATH, new Buffer(data.Body, 'binary'), (err) => {
                if (err) { callback(err, err.stack); }

                // ここでresize
                im.convert(convertParams, function (err, stdout, stderr) {
                    if (err) { callback(err, 'im convert'); }
                    console.log('adsfadsfasdfasdfasdfasdfasdfasdfasdfasdfadsfs');
        
                    fs.readFile(CONVERTED_TMPFILE_NAME, function (err, converted_data) {
                        if (err) { callback(err, converted_data, 'converted tempfile read'); }
        
                        console.log(bucket);
                        console.log(event.outPutName);
                   
                        // resizeした画像をS3に上げる
                        s3.putObject({
                            Bucket: bucket,
                            Key   : event.outPutName,
                            Body  : new Buffer(converted_data, 'binary'),
                            ContentType: data.ContentType
                        }, function (err, res) {
                            if (err) { callback(err, 's3 putObject'); }

                            // URLを返す場合はこちら
                            // callback(null, event.outPutName)

        // binary dateを返す
                            callback(null, new Buffer(converted_data, 'binary').toString('base64'))

                            // これがないと、S3に上げる前に、lambdaが終わっちゃう
                            context.done();
                        });
                    });
                });
            });
        }
    });
};

テスト

lambda上でテストする場合は下記

{
  "parameter": "bucket_name/folder_name1/folder_name2/600x600/hogehoge.jpg"
}

性能

10MBくらいの画像でも、5秒くらいは一応返してくれる