API GatewayとLambdaでサーバーレスに画像をリサイズする

ゴール

リサイズ前の画像はCloudFrontS3
f:id:acquapazza:20180127231406p:plain:w400

リサイズ後の画像は CloudFrontAPI GatewayLambdaS3
f:id:acquapazza:20180127231459p:plain:w700
で取得する。
※サイズの値はクエリパラメータで指定できるようにする。


S3

aws-image-testという名のバケットを作成。
imgフォルダ配下にaws.jpgをアップロードし、公開するを実行。
f:id:acquapazza:20180127232043p:plain


Lambda

関数の作成で設計図からs3-get-object(nodejs6.10)を選択。
pythonでもOK
f:id:acquapazza:20180127232215p:plain

名前、ロール名は任意。
ロールはテンプレートから新しいロールを作成
ポリシーテンプレートはS3オブジェクトの読み取り専用アクセス権限を選択。
f:id:acquapazza:20180127232333p:plain

S3の設定。LambdaをどのS3と連携させるかということ。
今回はaws-image-testと入力し、関数を作成 を実行。
f:id:acquapazza:20180127232441p:plain

関数作成後、左側にS3が紐づいているが、不要なので削除。
f:id:acquapazza:20180127232511p:plain

関数コード には下記を実装。

'use strict';

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

exports.handler = (event, context, callback) => {
  const key = event.filename;
  const bucket = "aws-image-test";
  const params = {
    Bucket: bucket,
    Key: key,
  };

  s3.getObject(params, (err, data) => {
    if (err) {
      callback(`Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`);
    } else {
      im.resize({
        srcData: data.Body,
        format: "jpg",
        width: event.width
      }, function(err, stdout, stderr) {
        if (err) {
          callback('resize failed', err);
        } else {
          callback(null, new Buffer(stdout, 'binary').toString('base64'))
        }
      });
    }
  });
};


試しにテストを実行。
下記テストイベントを設定し、実行後に成功が確認できればLambdaの準備は完了。
f:id:acquapazza:20180127232901p:plain


API Gateway

API名にaws-image-test、バイナリサポートの設定にimage/*を設定。
f:id:acquapazza:20180127232956p:plain

アクションからリソースの作成を選択。
リソース名、リソースパスにresizeと入力。
f:id:acquapazza:20180127233032p:plain

再度アクションからリソースの作成を選択。
2つのチェックボックスにチェックを入れて、リソース名にfilepath、リソースパスに{filepath+}と入力。
f:id:acquapazza:20180127233130p:plain

するとANYOPTIONSというメソッドができるが、ANYは不要なので削除。
GETが必要なので、アクションからメソッドの作成を選択し、GETメソッドを作成。
f:id:acquapazza:20180127233246p:plain

GETメソッドの統合リクエスト本文マッピングテンプレートから下記を定義。
f:id:acquapazza:20180127233331p:plain

{
  "filepath": "$input.params('filepath')",
  "width": "$input.params('width')"
}


CloudFront

Create Distributionから、作成したS3をDefaultに指定して作成。
Origin Custom Headersに、Content-Type: image/jpeg, Accept: image/jpegを指定。
f:id:acquapazza:20180127233439p:plain

Create Distribution実行後、Create Originから下記のようにLambdaを指定。
Origin Custom HeadersはS3と同様。
f:id:acquapazza:20180127233514p:plain

/resize/*のパスパターンでLambdaに振りたいので、Create Behaviorから下記のように設定。
Default TTLはキャッシュしない場合は0にする。
f:id:acquapazza:20180127233544p:plain ※CloudFrontは更新に時間がかかるので、しばらく待つ。


実行

これで、
リサイズ前の画像取得:  https://hogehoge.cloudfront.net/img/aws.jpg
リサイズ後の画像取得:  https://hogehoge.cloudfront.net/resize/img/aws.jpg?width=100
が可能になりました。