【Lambda】バイナリデータは4.5MBくらいまでしか返却できない

# 要約
- Lambdaの呼び出しペイロード制限は6MBだが、バイナリデータを返却する場合は大体4.5MBまでのデータしか返せない
    - Lambdaでバイナリデータを返却する場合はbase64にエンコードする必要があるため

# Lambdaのペイロードの制限
Lambdaでは呼び出しペイロード[^1]は6MBまでという制限が付いている[^2]。

> 呼び出しペイロード (リクエストとレスポンス): リクエストとレスポンスにそれぞれ 6 MB (同期)
引用元: https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html

しかし、5MB弱のバイナリデータ(画像ファイルとか)を返却しようと思ってもできない。
この後の項で実験する。

# Lambdaでバイナリデータを返却するWeb APIを実装する
## 概要
Lambda(関数URL)を用いて、画像を取得できるWeb APIを実装した。
エンドポイント`/image`にGETリクエストしたとき、クエリパラメータ`name`にファイル名を指定すると、そのファイルを取得する。
ファイルはS3バケットから取得する。

Lambdaの仕様として、バイナリデータを返却する場合はbase64にエンコードする必要がある点に留意[^3]。
> 注: バイナリデータの場合は、Lambda 関数の出力で、;isBase64Encoded プロパティを true に設定する必要があります。body プロパティには、base64 でエンコードされたバイナリメディアも含まれている必要があります。詳細については、「API Gateway における REST API のバイナリメディアタイプ」を参照してください。
引用元: https://repost.aws/ja/knowledge-center/api-gateway-binary-data-lambda?utm_source=chatgpt.com

## ソースコード
```python
import json
import boto3
import base64
from botocore.exceptions import ClientError

s3_client = boto3.client('s3')
BUCKET_NAME = 'bucket-name'

def lambda_handler(event, context):
	image_name = event.get('queryStringParameters', {}).get('name', None)
	response = s3_client.get_object(Bucket=BUCKET_NAME, Key=image_name)
	image_data = response['Body'].read()

    # 重要: Lambdaでバイナリデータを返す際はbase64へのエンコードが必須
	encoded_image_data = base64.b64encode(image_data).decode('utf-8')
	
	return {
		'statusCode': 200,
		'headers': {
			'Content-Type': 'image/jpeg',
		},
		'body': encoded_image_data,
		'isBase64Encoded': True,
	}
```

## 配置するバイナリデータ
画像をS3バケットに2つ配置する。
4.6MBと4.8MBのファイルサイズを持つJPEG形式の画像ファイル。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2960758/fa19afd0-d1b5-4b74-aa5f-c0fe1b0b7830.png)

# 実装したWeb APIからバイナリデータの取得を試みる
4.6MBのバイナリデータを取得する。
成功する。
```
$ curl --dump-header - "https://foobarfoobarfoobar.lambda-url.ap-northeast-1.on.aws/?name=dummy_image_4p6MB.jpg"
HTTP/1.1 200 OK
Date: Tue, 11 Mar 2025 16:17:49 GMT
Content-Type: image/jpeg
Content-Length: 4600000
Connection: keep-alive
x-amzn-RequestId: 8b5150ef-9378-458e-b060-4d3f130b89e2
X-Amzn-Trace-Id: Root=1-67d0622c-3c8559fe06e620ef7b853e7f;Parent=727baee9259e2775;Sampled=0;Lineage=1:4b1c4ed4:0

Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.
```

4.8MBのバイナリデータを取得する。
失敗する。
```
$ curl --dump-header - "https://foobarfoobarfoobar.lambda-url.ap-northeast-1.on.aws/?name=dummy_image_4p8MB.jpg"
HTTP/1.1 502 Bad Gateway
Date: Tue, 11 Mar 2025 16:17:58 GMT
Content-Type: application/json
Content-Length: 21
Connection: keep-alive
x-amzn-RequestId: 2a26a20f-e58d-49a3-9eca-ae596b0cf22a
X-Amzn-Trace-Id: Root=1-67d06234-214cf04e5aed8a96244936b2;Parent=1fbc00920a520653;Sampled=0;Lineage=1:4b1c4ed4:0

Internal Server Error
```

# なぜ失敗するか
返却時にbase64へエンコードを行うことでデータのサイズが増え、ペイロード制限に抵触しているから。
base64へエンコードを行うと元のサイズから約1.33倍に増える。

Lambdaのペイロード制限が6MBなので、6/1.33 ≒ 4.51MB程度のバイナリデータしか返せないということになる。

4.6MBのバイナリデータ取得には成功したので、多少ブレは出ると思われる。


[^1]: 呼び出しペイロード...リクエストとレスポンスに乗せるデータのこと。
[^2]: RESPONSE_STREAMの場合は20MBまで増える。6MBなのはBUFFEREDの時のみ。
[^3]: API Gatewayを利用した場合でも関数URLを利用した場合でもこの仕様。