AWSでランダムな画像を返すURLを作りました(Stravaのワークアウトのシェアのため)
HTMLのIMG要素のSRC属性に設定しておけば、ランダムに画像を表示するURLです。 複数の画像をAWS S3のバケットに置いておき、CloudFront - API Gateway - Lambda Function というルートで画像を返します。
当初は上の画像をそのURLから取ってきていたのですが、AWSの請求額がグイグイ上がっていくので(CloudFrontのせいだと思う)怖くなって固定画像に変えました。スンマセン。
Stravaのワークアウト完了時に、IFTTTを使って結果(距離、時間など)をTwitterに流していて、走行経路の地図(GoogleMaps)を添付しているのですが、常に「Image not found」となっており(今思えばセキュリティ設定に関連しているのかもしれない)、アクセスするたびに画像が入れ替わるURLを作ればいいやと、初夏の風吹く4月の土曜日に、朝メシ食わずにAWSでポチポチ作業・・・
API Gateway、Lambda Function、S3 Bucket で簡単にできると思っていたのですが、ブラウザのIMGタグで表示させようとするとリクエストヘッダにAcceptで受け入れ可能なMIME Typeを指定しておかなければならなくて、そのためには CloudFront を使わなければならないらしい。なるほど勉強になりました。
Lambda Function
Lambda Functionは、S3のバケットの特定のフォルダー以下から画像ファイルをリストして、その中からランダムに選んだ画像を返します。 バケット名とフォルダの名前は環境変数で設定しています。
const AWS = require("aws-sdk"); const S3 = new AWS.S3(); const bucket = process.env["BUCKET"]; const keyPrefix = process.env["KEYPREFIX"];// `strava-tweet-photos` exports.handler = async (event) => { //S3の画像をリスト const listResult = await S3ListObjects({ Bucket: bucket, Prefix: keyPrefix }); //フォルダ以外のキーの配列に変換 const keys = listResult.Contents .map(item => item.Key) .filter(key => (key !== keyPrefix)); //ランダムに一つ選ぶ const index = randomInt(keys.length); const key = keys[index]; //画像を取得 const image = await S3GetObject({ Bucket: bucket, Key: key }); //HTTPレスポンスを返す const response = { statusCode: 200, headers: { 'Content-Type': image.ContentType }, body: image.Body.toString('base64'), isBase64Encoded: true, }; return response; }; const S3ListObjects = params => new Promise( (resolve, reject) => S3.listObjects(params, (err, data) => (err ? reject(err) : resolve(data)))); const S3GetObject = params => new Promise( (resolve, reject) => S3.getObject(params, (err, data) => (err ? reject(err) : resolve(data)))); const randomInt = max => Math.floor( Math.random() * Math.floor(max));
API Gateway REST API
適当なリソースを作ってGETメソッドを作り、「統合リクエスト」で「Lambda統合」を選択して、上のLambdaを指定します。 Lambdaの戻り値をそのままメソッドレスポンスへ渡すので、「統合リクエスト」の「Lambdaプロキシ統合」にチェックを入れます。 ブラウザで表示するだけなら、CORSは対応しなくて良いですね。
CloudFront Distribution
上のREST APIをデプロイし、そのエンドポイント(例: xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com
)を Origin に指定してCloudFrontディストリビューションを作ります。
Origin Path にはAPIをデプロイしたステージ名を指定(例:/production
)して、Origin Protocol Policy を HTTPS Only
とします(API Gateway の REST APIなので)。
今後の話
画像のサイズを指定できたり、画像のEXIFを読み取って、アクセスした日に近い画像を返すとか、いろんな機能拡張が思い浮かびます。 ちょっとずつ楽しみながら実装していきたいなーと思いました。