#mynavi(AWSメモ)
#setlinebreak(on);

* 目次 [#m682af77]
#contents
- 参考
-- https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html
-- https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-payload-encodings.html
-- https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-controlling-access-to-apis.html
-- https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-template.html
-- https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessapi
-- https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
- 関連
-- [[AWS CloudFormationメモ]]
-- [[Lambda&API GatewayをCloudFormationで作成する]]
-- [[1つのAWS LambdaでSPA(CloudFront編)]]

* 概要 [#oc71e755]
#html(<div style="padding-left: 10px;">)

API Gateway & Lambda でバイナリレスポンスを返却する場合の覚え書き。

ポイントは以下の3点
- Lambda のレスポンスで isBase64Encoded: True を指定しつつ、Base64エンコードされたデータを返却する
- API Gateway の バイナリメディアタイプ で対象のコンテンツタイプを指定する
- リクエスタから Accept ヘッダで対象のコンテンツタイプを受け取っていない場合は Base64デコードされない。&br; ※Base64エンコードされたデータがそのまま返却される

#html(<div style="color:red;">)
注意)
 ただし、あまり大きな画像を返却するような処理はメモリ消費が大きい為、書くべきではない。
 Lambdaのレスポンスサイズ制限や費用も考慮する事。
 多分、バッチでS3に出力しておいて、API Gateway から呼ばれる Lambdaでは、期限付きの署名付きURLを発行するぐらいにしておく方が良い。
#html(</div>)

#html(</div>)
// 概要 END

* フォルダ/ファイル構成 [#i6afbb5a]
#html(<div style="padding-left: 10px;">)

#html(<div style="border:1px solid #333; padding:10px; display: inline-block;">)

#html(<div style="display:inline-block;">)
/work
 /template.yml
 /src
  /index.py
  /sample1.png
#html(</div>)
#html(<div style="display:inline-block; padding: 0 30px; vertical-align: top;">)
 
・・・ cloudformationテンプレート
・・・ Lambdaソース格納用
・・・ Lambda本体(メインハンドラ)
・・・ 返却する画像
#html(</div>)

#html(</div>)


#html(</div>)
// フォルダ/ファイル構成 END


* Lambda [#mf82fa5a]
#html(<div style="padding-left: 10px;">)

./src/index.py
#mycode2(){{
import base64


def handler(event, context):

  with open("sample1.png", "rb") as f:
    bytes_image = f.read()
    encoded_image = base64.b64encode(bytes_image).decode("utf-8")

  return {
    "statusCode": 200,
    "headers": {"Content-Type": "image/png"},
    "isBase64Encoded": True,
    "body": encoded_image
  }
}}

#html(</div>)
// Lambda

* CloudFormationテンプレート [#e9427fc1]
#html(<div style="padding-left: 10px;">)

[参考]
https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md

./template.yml
#myterm2(){{
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: "バイナリデータを返却する API Gateway & Lambda"

Resources:
  ImageResponseApi:
    Type: AWS::Serverless::Api
    Properties:
      Name: ImageResponseApi
      StageName: Prod
      BinaryMediaTypes: 
        - "image/*"

  ImageResponseFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ImageResponseFunction
      CodeUri: ./src
      Handler: index.handler
      Runtime: python3.6
      Events:
        GetRoot:
          Type: Api 
          Properties:
            RestApiId: !Ref ImageResponseApi
            Path: /
            Method: get 

Outputs:
  ImageResponseApiUrl:
    Value: !Sub "https://${ImageResponseApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
    Export:
      Name: ImageResponseApiUrl
}}

#html(</div>)

* デプロイ [#jf7890e1]
#html(<div style="padding-left: 10px;">)
参照: [[CloudFormation実行用のシェル]]
#html(</div>)

* 確認 [#u91f8f4f]
#html(<div style="padding-left: 10px;">)

ブラウザからURLを直接叩いても、Acceptヘッダは付かないので注意が必要。

curl から Acceptヘッダ付きのリクエストを発行して確認。(レスポンスはバイナリなのでファイルに出力)
#myterm2(){{
curl -H "Accept: image/png" --output test.png 対象のAPIのURL
}}

もしくは、以下の様な簡単なHTMLで確認しても良い。
#mycode2(){{
<!doctype html>
<html>
<img src="対象のAPIのURL">
</html>
}}

#html(</div>)

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS