はじめに
画像をS3バケットにアップロードする場合であれば、AWS CLIコマンドの実行でアップロードできると思いますが、APIコールする形でS3にアップロードできないか気になったので試してみました。
本記事では、API Gatewway ⇒ Lambda ⇒ S3バケットのリソースを構築し、base64にフォーマットした画像をAPIコール時のリクエストbodyに乗せてAPIを叩いてみようと思います。
AWS CLIでS3へファイルアップロードする場合
aws s3 cp 画像ファイル.jpg s3://バケット名/画像ファイル.jpg
環境
- Python:3.13
- Postman:11.43.3
- git bash
ゴール
PostmanからAPIをコールしS3に画像がアップロードされてることを確認する。
リクエストbodyにはbase64形式にフォーマットした3つの画像形式(jpeg、png、webp)を載せる。
APIの叩き方はcurl、Postman、ブラウザのfetch apiの3つでやってみます。

1. Lambdaの作成
APIからのリクエストを処理してS3にファイルをアップロードするLambda関数を作成します。
以下の設定も行っています。
- IAMに追加するアクション:"s3:PutObject"
- 環境変数(S3_BUCKET_NAME)にバケット名設定
import json
import base64
import boto3
import os
from request_processor import process_request
from file_processor import process_file
from s3_uploader import upload_to_s3
from response_generator import create_response
def lambda_handler(event, context):
# イベントが文字列の場合は JSON にパース
if isinstance(event, str):
event = json.loads(event)
try:
# リクエスト処理
body = process_request(event)
# ファイル処理
file_name, file_data, content_type = process_file(body)
# S3アップロード
bucket_name = upload_to_s3(file_name, file_data, content_type)
# 成功レスポンス
return create_response(200, {
'message': 'アップロード成功'
})
except ValueError as e:
# バリデーションエラー
print(f'バリデーションエラー: {str(e)}')
return create_response(400, {'error': str(e)})
except Exception as e:
# その他のエラー
print(f'サーバーエラー: {str(e)}')
return create_response(500, {'error': f'サーバーエラー: {str(e)}'})
lambda_handlerから呼び出してる関数は以下のスクリプトに記載してます。
request_processor.py
import json
import base64
def process_request(event):
# イベントのログ出力
print(f"イベント内容: {json.dumps(event)}")
# リクエストボディを取得
body = event.get('body', '')
print(f"リクエストボディ: {body}")
# デコード
if event.get('isBase64Encoded', False):
body = base64.b64decode(body).decode('utf-8')
print(f"Base64エンコードされたボディをデコード後: {body}")
# リクエストが文字列の場合はJSONに変換
if isinstance(body, str) and body:
body = json.loads(body)
print(f"デコード後のボディ: {json.dumps(body)}")
return body
file_processor.py
import base64
def process_file(body):
# リクエストからファイル名とファイルデータを取得
file_name = body.get('file_name', '')
file_data = body.get('file_data', '')
print(f"ファイル名: {file_name}")
# 入力検証
if not file_name:
raise ValueError('ファイル名が指定されていません')
if not file_data:
raise ValueError('ファイルデータが指定されていません')
# Base64エンコードされたデータをデコード
decoded_data = base64.b64decode(file_data)
# ファイルの種類を判断
content_type = 'image/jpeg'
if file_name.lower().endswith('.png'):
content_type = 'image/png'
elif file_name.lower().endswith('.webp'):
content_type = 'image/webp'
return file_name, decoded_data, content_type
s3_uploader.py
import boto3
import os
def upload_to_s3(file_name, file_data, content_type):
# S3バケット名
bucket_name = os.environ.get('S3_BUCKET_NAME')
# S3クライアントを作成
s3_client = boto3.client('s3')
# S3にファイルをアップロード
print(f"S3にアップロード中: バケット名={bucket_name}, ファイル名={file_name}, コンテンツタイプ={content_type}")
s3_client.put_object(
Bucket=bucket_name,
Key=file_name,
Body=file_data,
ContentType=content_type
)
return bucket_name
response_generator.py
import json
def create_response(status_code, body):
return {
'statusCode': status_code,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps(body)
}
2. API Gateway
次に、Lambdaを呼び出すためのREST APIを作成します。
-
メソッドタイプ:POST
-
統合タイプ: Lambda 関数
-
Lambda
-
1. Lambdaの作成
で作成したLambdaを選択 - プロキシ統合を選択
-
-
バイナリサポートの設定
- image/jpeg
- image/png
- image/webp
今回は検証なのでCORSで許可するオリジンは全て許可(* で設定)しておきます。
APIのデプロイ後、ステージからエンドポイントURLが確認できるのでメモしておきます。
3. 動作の確認
ここまでできたらメモしたAPIのエンドポイントをPostmanからPOSTメソッドでコールしてみます。
リクエストBodyに渡す用の画像を準備
今回アップロードする画像は次のものです。
以下のコマンドで画像をbase64形式にフォーマットします。
コマンドの実行にはgit bashを利用します。
# JPEG画像の場合
base64 -w 0 test-image.jpg > test-image-base64.txt
# PNG画像の場合
base64 -w 0 test-image.png > test-image-base64.txt
# WebP画像の場合
base64 -w 0 test-image.webp > test-image-base64.txt
PostmanでAPIコールする場合
Postmanを使って画像をアップロードしてみます。
アップロードする画像はjpg
形式のものです。
Postメソッドを作成します。
- 「Headers」タブで以下を設定
- Content-Type:application/json
- 「Body」タブを選択し、「raw」を選択、JSONを選択して以下のような内容を入力
{
"file_name": "test-profile.jpg",
"file_data": "base64形式に変換した値"
}
Send
を押下してAPIをコールすると、200
で返ってくるのが確認できました。
S3バケットを確認するとjpeg画像アップロードできていました。
curlでAPIコールする場合
コマンドラインからcurlを使ってAPIをコールする場合は以下のようにします。
アップロードする画像はpng
形式のものです。
リクエストBodyを記載したJSONをファイルを作成しそれをcurlで指定してAPIコールします。
{
"file_name": "test-profile2.png",
"file_data": "base64形式に変換した値"
}
curl -X POST \
APIのエンドポイントURL \
-H "Content-Type: application/json" \
-d @request.json
S3バケットを確認するとpng画像アップロードできていました。
chromeのfetch APIからアップロードする場合
次はchromeブラウザのfetch APIを使ってアップロードしてみます。
スタイリングはtaildwind cssを使用しています。
ブラウザから画像選択+fetch apiをコールするための簡易的なHTMLを作成します。
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>画像アップロード</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
</head>
<body class="p-4">
<div class="max-w-md mx-auto bg-white p-6 rounded shadow">
<h1 class="text-xl font-bold mb-4">画像アップロード</h1>
<input class="mb-4 w-full" type="file" id="fileInput">
<button id="uploadButton" class="w-full bg-blue-500 text-white py-2 px-4 rounded">
アップロード
</button>
<div id="status" class="mt-4"></div>
</div>
<script>
// APIエンドポイント
const API_ENDPOINT = 'APIエンドポイントURL';
document.getElementById('uploadButton').addEventListener('click', () => {
const file = document.getElementById('fileInput').files[0];
if (!file) return;
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
// Base64データ部分のみを抽出
const base64Data = reader.result.split(',')[1];
fetch(API_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
file_name: file.name,
file_data: base64Data
})
})
.then(response => response.json())
.then(data => {
document.getElementById('status').textContent = 'アップロード成功';
});
};
});
</script>
</body>
</html>
vscodeのLive ServerでWebサーバを起動し画面を立ち上げます。
ファイルを選択
からWebp画像を選択しアップロードボタンを押下すると、数秒待った後にアップロード成功
の文言が表示されました。
S3バケットを確認するとWebP画像アップロードできてました。
PDFファイルをアップロードする場合
PDFファイルも同様にAPIを通じてS3にアップロードできるのか気になったので追加で確認してみました。
Lambdaの修正
Lambda関数のfile_processor.py
にPDFのContent-Type
を追加します。
# ファイルの種類を判断
content_type = 'image/jpeg'
if file_name.lower().endswith('.png'):
content_type = 'image/png'
elif file_name.lower().endswith('.webp'):
content_type = 'image/webp'
+ # PDFのContent-Typeを追加
+ elif file_name.lower().endswith('.pdf'):
+ content_type = 'application/pdf'
API Gatewayの修正
API GatewayのバイナリサポートにPDFのMIMEタイプを追加します。
- application/pdf
PDFをbase64にフォーマット
以下のようにしてPDFをbase64にフォーマットします。
base64 -w 0 test-document.pdf > test-document-base64.txt
リクエストBodyは以下のようにrequest.jsonに記載しておきます。
base64形式に変換された値をtest-document-base64.txt
からコピーして張り付けておきます。
{
"file_name": "test-document.pdf",
"file_data": "base64形式に変換した値"
}
APIコール
今回はcurlでAPIをコールしてみます。
curl -X POST \
APIのエンドポイントURL \
-H "Content-Type: application/json" \
-d @request.json
S3バケットを確認してみる
S3バケットを確認するとPDFもアップロードできてました。
4. 終わりに
今回試してみた限りではうまくいっていたので、ペイロードのサイズがオーバーしてしまう事象に遭遇はしなかったですが、Lambdaのペイロードサイズについてデベロッパーガイドに記載がありました。
リクエストとレスポンスにそれぞれ 6 MB (同期)
参考:https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html
あまりに大きいデータを渡すときには注意しようと思います。