画像をランダムに返したいだけのシンプルなAPIを作ろうと思ったけど、とてつもなくハマったのでメモ。
ソースは下記においてあります。
https://github.com/celeron1ghz/apigateway-random-image
lambdaからの値の返し方に作法がある
これはlambdaのコードの中身の話。
以前の API Gateway
は統合レスポンスでマッピングテンプレートを色々と作らなくてはいけなかったのですが、 Lambdaプロキシ
が追加され、チェックを入れるだけで共通的な処理はいろいろ設定しなくて良くなりました。
その代わりフォーマットに則ったデータの返し方をしないと自動で処理してくれないので下記のルールに従います。
lambdaからの画像データはbase64にして返す
lambdaプロキシがうまい具合にbase64からバイナリに戻してくれるので、ここではbase64で返します。var.toString('base64')
とすればbase64にできるので大したことないですね。
lambdaで戻すべきキーがある
下記のキーを含まないといけません。
- statusCode
- headers
- body
- isBase64Encoded = true
body
には画像ファイルの内容をbase64にしたもの、isBase64Encoded = true
は設定しないと変換対象として見てくれないっぽいので忘れずに。
だいたいこんな感じになると思います。
callback(null, {
statusCode: 200,
headers: { 'Content-Type': 'image/jpeg' },
body: data.Body.toString('base64'),
isBase64Encoded: true,
});
統合リクエストでLambdaプロキシ統合をチェックする
これはAPI Gatewayの設定の話。
上記で設定した内容をいい感じで処理してくれるようにするためにチェックを入れます。
バイナリサポートにMIMEを設定する
これもAPI Gatewayの設定の話。
上記ではうまい具合にバイナリに戻してくれると書きましたが、実際はリクエストのAccept
ヘッダに指定されたMIMEが、このバイナリサポート
で設定したMIMEの中に存在するとバイナリ変換が動きます。
自分はJPGしか使わなかったので image/jpeg
しか指定しませんでしたが、そのほかのフォーマットの画像を使うときは適宜増やしましょう。
cloudfrontを使う
ここまででAPI Gatewayで自動で振られるアドレスにブラウザでアクセスすれば画像が落ちてくる!
と思いきや落ちてこないんだなこれが。
上記に書いた通り、リクエストの Accept
ヘッダにMIMEを指定してあげないといけませんが、ブラウザの普通のアクセスでそんなことはできないですね。
なお、下記のようにcurlとかで Accept
を指定してあげるとちゃんとファイルが落ちてきます。
curl -H "Accept: image/jpeg" "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/image" > moge.jpg
どうすんだよ、っていう解決案がCloudfrontです。
Cloudfrontがファイルを取りに行く時のヘッダを指定できるので、そこで Accept
ヘッダを指定するとAPI Gatewayから画像が取れるので、cloudfrontのアドレスにアクセスすれば画像が取れます。
curl "https://xxxxxxxxxxxxxx.cloudfront.net" > moge.jpg
めでたしめでたし。
まとめ
- Lambda + API Gatewayで画像を扱おうとすると(最初は)だいぶめんどくさい
- なぜ画像を扱おうとするだけでCloudfrontが出てくるのか(従量課金だからそんなに気にしなくていいけど大げさすぎる感がある)