画像の扱いについて、当初のS3利用からDB保存へ移行し、さらにその実装方式を最適化しました。これまでの3つの変遷をまとめます。
1. 初期:AWS S3 ストレージ
外部ストレージ(S3)に実体を保存し、DBにはパス(URL)のみを保存する一般的な構成です。
2. 変更後:DB保存(Base64方式)
S3をやめ、DBに直接データを保存するように変更しました。
まずは画像を文字列化して扱う実装を採用しました。
- 保存先: データベース
- 形式: Base64文字列(TEXT型)
[ブラウザ]
画像ファイル選択
↓
FileReader.readAsDataURL() // ← Base64に変換
↓
"data:image/png;base64,iVBORw0KGgo..." (文字列)
↓
JSON { avatar: "iVBORw0KGgo..." }
↓
[API] Laravel
↓
DB TEXT型 に文字列保存3. 現在:DB保存(Buffer/Binary方式)
同じDB保存ですが、文字列変換をやめ、バイナリデータのまま扱うことで容量と処理を最適化しました。
- 保存先: データベース
- 形式: バイナリ(BLOB型)
[ブラウザ] 画像ファイル選択
↓
FormData.append(file)
↓
multipart/form-data で送信
↓
[サーバー内部 /tmp] PHPが一時ファイルを自動作成
↓
[API] Laravel $request->file('avatar')
↓
file_get_contents() // 一時ファイルをメモリ(Buffer)へ読み込み
↓
DB BLOB型 にバイナリ保存[ブラウザ] 画像URLへアクセス(例: <img src="/api/avatar/1">)
↓
GETリクエスト
↓
[API] Laravel DBからBLOBデータを取得
↓
ヘッダー設定 (Content-Type: image/png)
↓
バイナリデータをそのままレスポンス
↓
[ブラウザ] 画像として描画
Blobとは
Binary Large OBject(バイナリ・ラージ・オブジェクト)の略です。
目次
参考記事
GitHub

laravel-rds/app/Http/Controllers/Api/UserController.php at main · idw-coder/laravel-rds
Contribute to idw-coder/laravel-rds development by creating an account on GitHub.
Qiita


Node.jsで画像アップロードを受けつけるサーバー - Qiita
カメラ撮影したものをサーバーにアップロードする仕組みを作っています。 クライアント側コードはまた別途 今回はサーバー側のミニマム構成っぽいものを書いてみたのでメモ...
画像アップロードエラー
【事実】
成功するケース
- 小さい画像(推測: 1-2MB以下)
- OPTIONS →
Access-Control-Request-Headers: authorization,content-type - OPTIONS Response → CORSヘッダーあり
- POST → 200 OK
失敗するケース
- 大きい画像(推測: 2MB以上?具体的なサイズ不明)
- OPTIONS →
Access-Control-Request-Headers: authorizationのみ - OPTIONS Response → 空(CORSヘッダーなし)
- POST → 送信されない
- エラー:
Unexpected token '<', "<!-- Malfo"... is not valid JSON
【環境設定】
PHP
upload_max_filesize: 100M✅post_max_size: 100M✅
Laravel
- バリデーション:
max:5120(5MB) ✅ - ルート: POST
/api/profile✅ - CORS:
HandleCorsミドルウェア使用
フロントエンド
- サイズチェック: コメントアウト(無効)
- FormData使用 ✅
fetchWithAuth: FormData時はContent-Typeを設定しない ✅
【試したこと】
- ✅ キャッシュクリア (
route:clear,config:clear, etc) - ✅ バリデーションサイズ上限を2MB→5MBに変更
- ✅ PHP設定確認(問題なし)
- ❌ nginx設定確認(未実施)
- ❌ 具体的な境界値の特定(何MBで失敗するか未確認)
【原因として考えられること】
仮説1: ブラウザ側の問題
- 大きいファイルの場合、ブラウザがOPTIONSリクエストで
content-typeを含めない - 理由不明(ブラウザのバグ?)
仮説2: Laravel HandleCorsの不安定性
Access-Control-Request-Headersにcontent-typeがない場合、正しくレスポンスしない- 小さいファイルでは偶然動いている
仮説3: nginx/タイムアウト
- 未確認(可能性は低い、OPTIONSが失敗しているため)
【次のステップ候補】
- 境界値の特定 – 何MBで失敗するか正確に測定
- nginx設定確認 –
client_max_body_sizeなど - fruitcake/laravel-cors導入 – 安定したCORS処理
- 別ブラウザでテスト – Chrome以外(Firefox/Safari)で同じ現象か確認