ぽっちぽちにしてやんよ

技術ネタとかアプリに関する話とか

AWS Lambdaで画像を返すAPIを実装する

こんにちは!ぽち@pchwです!

AWS Lambdaは気軽に処理が書けて,API Gatewayと組み合わせるAPIサーバを作るのにとても便利です.

色々とサービスを作っていると,HTTPのエンドポイントを叩くと,画像が返ってきて欲しいケースなどが出てきます.(例えば,画像のURLを投げるとバックエンド側で画像を取得して加工を施して返すなど)

お行儀のいい方法であれば,処理を行ったあとにAWS S3にアップロードし,APIレスポンスはそのURLが格納されたjsonが返り,クライアント側がそのURLに対して取得しに行くというのが筋な気がしますが,そんなに大した画像処理をせずに大量の人が使う可能性がある時などに「そのまま画像を返したいな〜」となることがあります.

やっていきましょう.

まず,めんどくさいので,Serverless Frameworkで雛形を作ります.

$ serverless create --template aws-nodejs
$ more handler.js 
module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  callback(null, response);
};

デフォルトの handler.js の処理は, messageinput というキーを持ったJSONを返す造りになっています.

単純に考えれば,ここに読み込んだ画像のBufferを入れればいいんじゃないか?と思います.

しかし,callbackの第二引数に渡すものはJSON.stringifyしていることから分かるように,文字列でないといけません.

なら.toString()だ!という考えになりますよね?

module.exports.hello = (event, context, callback) => {
  const fs = require('fs');
  fs.readFile('./assets/noimage.png', (err, image) => {
    const response = {
      statusCode: 200,
      body: new Buffer(image).toString('base64')
    };

    callback(null, response);
  });
});

しかし,これでも上手くいきません.

どうすれば良いかとというと,

  1. ちゃんと画像を返すということをヘッダーに書く必要がある
  2. API Gatewayの設定で画像を返すという設定をしないといけない

の二点が追加で必要です.

module.exports.hello = (event, context, callback) => {
  const fs = require('fs');
  fs.readFile('./assets/noimage.png', (err, image) => {
    const response = {
      statusCode: 200,
      headers: {
            'Content-Type': "'image/png'",
        },
      body: new Buffer(image).toString('base64'),
        isBase64Encoded: true
    };

    callback(null, response);
  });
});

これで処理はOKです.

API Gatewayの方はAWS Consoleでも可能ですが,Serverless Frameworkのプラグインを使うのが楽です.

$ npm install serverless-apigw-binary --save-dev

でプラグインをインストールし,serverless.ymlを編集します.

plugins:
  - serverless-apigw-binary # for custom.apigwBinary

pluginsセクションに追加し,customセクションで設定を記述します.

custom:
  apigwBinary:
    types: #list of mime-types
      - 'image/*'
      - 'text/html'

あと,ルートにアクセスされたら画像を返すようにHTTPエンドポイントの設定も記述しておきましょう.

functions:
  hello:
    handler: hello
    timeout: 15
    events:
      - http:
          path: /
          method: get

これで $ serverless deploy すればAPI Gatewayの設定が行われて,バイナリを返せるようになります.

あとはクライアント側で

<img src="DeployしたやつのURL" alt="image"/>

のようにすれば画像が読み込まれるはずです.

画像返すAPIバンバン作っていきましょう!