頭ん中

しがないITエンジニアが、考えた事を書きます。

API GatewayとLambdaでバイナリデータを扱う

画像などのバイナリデータをアップロードするREST APIを作ります。詳細はAmazon API Gateway 開発者ガイドのバイナリペイロードをサポートするという章を読むこと。バイナリの扱いにちょっと戸惑ったのでメモ。

前提

  • API GatewayとLambdaの基本的な設定内容は省略

API Gateway

バイナリメディアタイプ

API GatewayはデフォルトではテキストペイロードUTF-8エンコードされた JSON)として扱うため、バイナリペイロードで扱う場合は、API設定からバイナリメディアタイプを指定し、バイナリとして扱うContent-Typeを設定します。

今回はmp3をアップしたいのでaudio/mpegを指定

マッピングテンプレート

さっきの手順でバイナリペイロードとして扱うことにはなったんですが、そのままバックエンド(Lambda)までバイナリで渡せるのかなと思ったら、違ってた。

API Gatewayは、統合リクエスト本文マッピングテンプレートの設定で、バックエンドへの渡し方を決められます。ここでContent-Type:audio/mpegについてパススルーすれば、Lambdaにバイナリで渡るのかなと試したのですが、エラー。

{"message": "Could not parse request body into json: Unexpected character (\'\/\' (code 47)): maybe a (non-standard) comment? (not recognized as one since Feature \'ALLOW_COMMENTS\' not enabled for parser)\n at [Source: (byte[])\"\/9j\/4AAQSkZJRgABAQEASABIAAD\/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT\/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT\/wAARCAEqASwDASIAAhEBAxEB\/8QAHQABAAEFAQEBAAAAAAAAAAAAAAgBAgYHCQUEA\/\/EAEgQAAEDAwMDAgMFBQQHBQkAAAEAAgMEBREGByEIEjETQSJRYQkUMnGBFSNCkaEWUrHBFyYzYnKCkiRz0eHxJTRTY4OUo7Lw\/8QAHQEBAAEFAQEBAAAAAAAAAAAAAAECAwQFBgcICf\/EAC8RAQABAwMCAwYGAwAAAAAAAAABAgMEBREhMUEGBxITQlFhcYEUFZGhscEiM+H\/2gAMAwEAAhEDEQA\/\"[truncated 17128 bytes]; line: 1, column: 2]"}

結局どうやってもAPI GatewayからLambdaへはJSONオブジェクトで渡ることになるので、テキストとして処理しないと駄目みたいです。というこで、統合リクエストLambdaプロキシ統合の設定にチェックをしれて、ややこしいマッピングテンプレートの設定は省略します。

Lambda

API Gatewayからはbodyがbase64エンコードされたいつものJSONオブジェクトが来るので、それをデコードして処理します。簡単にS3に置くようなプログラムを実装すると以下。

bucket = 'xxxx'
s3 = boto3.resource('s3')

def lambda_handler(event, context):
    print(event)
    
    if event['isBase64Encoded']:
        date = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
        key = date + '.mp3'
        print(key)
        
        decode = base64.b64decode(event['body'])
        
        obj = s3.Object(bucket,key)
        res = obj.put(
            Body=decode,
            ContentType='audio/mpeg'
            )
        
        print(res)
        
        return {
            'statusCode': 200,
            'body': json.dumps(
                {
                    'message': 'uploaded.',
                    'file': key
                }
            ),
            'isBase64Encoded': False
        }
    else:
        return {
            'statusCode': 400,
            'body': json.dumps(
                {
                    'message': 'not binary file'
                }
            ),
            'isBase64Encoded': False
        }

参考にしたサイト