定義
ユーザーがアップロードした大きなファイル(画像・動画など)を アプリサーバを経由せず、クライアントがストレージへ直接アップロードさせたい ときに使う方式だ。サーバは「このアドレスへアップロードせよ」という特別な URL だけを発行し、実際のファイル転送はブラウザがストレージと直接行う。
まず用語を解くと:オブジェクトストレージ(S3 など)はファイルを丸ごと1つのオブジェクト(object)として保存するクラウドストレージ、バイナリは画像・動画のようにテキストではないファイルの生データを指す。
正確に言えば Presigned URL は、オブジェクトストレージの所有者であるバックエンドが自身の認証情報で 署名して(偽造できないよう暗号署名を付けて)発行する、時間制限・権限制限つきの使い捨て URL だ。この URL を受け取ったクライアントは、別途の認証(ログイントークンなど)なしに、署名に埋め込まれた範囲(どのメソッド・どの経路・いつまで)の中でのみストレージへ直接リクエストを送れる。つまり「誰が何をいつまでできるか」をバックエンドが前もって決め、URL 一行に収めた委任トークンだ。
なぜ必要か
大きなファイルをアプリケーションサーバ経由でアップロードすると、サーバがアップロードトラフィック全体を中継するためにメモリ・帯域・リクエスト時間を消費する。リクエスト本文サイズや実行時間が制限された環境(serverless-stateless-execution)では、この方式自体が限界にぶつかる。presigned URL は 権限発行(軽量なメタデータ要求) と 実際の転送(クライアント↔ストレージ直接) を分離する。サーバは小さなリクエストだけを処理し、重いバイナリはストレージが直接受け取る。
動作原理
標準的な流れは4段階の契約に整理できる。
| 段階 | 呼び出し | 役割 | 抜けると |
|---|---|---|---|
| ① presign | POST /files → { fileId, uploadUrl, fileUrl } | バックエンドが署名済みアップロードアドレスを発行 | アップロード開始不可 |
| ② PUT | PUT uploadUrl (body = バイナリ) | クライアント → ストレージ直接転送 | ファイルがストレージにない |
| ③ complete | PATCH /files/{fileId} | バックエンドに「アップロード終了」を通知 | 「ファイル未アップロード」エラー |
| ④ create | POST /resources (fileId 参照) | アップロード済みファイルでリソース生成 | リソース生成失敗 |
要は②と③の分離だ。ストレージは PUT 成功をバックエンドに 自動で知らせない。 だからバックエンドが「このファイルはもう使える」と認識するには、③ complete 通知が別途必要だ。
実務での適用
モック(mock)環境で②の PUT を飛ばしたいなら、グローバルなモックフラグではなく uploadUrl がダミーアドレスかどうか(例:example.com を含む) で判断すべきだ。そうすれば実際のバックエンド presign URL には常に PUT が実行される。
トレードオフ
- 長所:アプリサーバの負荷・帯域の節約、サーバレスの本文サイズ制限の回避、大容量/多数アップロードに有利。
- コスト:段階が増えてクライアントのロジックが複雑になり、有効期限・content-type などの署名条件をクライアントが正確に合わせなければならない。アップロード進捗・リトライ・部分失敗の処理もクライアントの責任になる。
使ってはいけない場合
- ファイルがごく小さく、メタデータと一緒に一度で送る方が単純なとき(段階分離の利得が小さい)。
- サーバがアップロード途中でコンテンツを検証・変換する必要があり、バイナリが必ずサーバを経由しなければならないとき。
よくある間違い
- ③ complete の欠落:②なしまたは③なしで④を呼び、「ファイルがまだアップロードされていない」というエラーに遭う。
uploadUrlとfileUrlの混同:書き込みアドレス(uploadUrl)へ PUT すべきなのに、読み取りアドレス(fileUrl)へ送る。- content-type の不一致:署名に含まれる条件と PUT ヘッダーが異なり、署名検証に失敗する。
- 有効期限の無視:発行後あまりに遅く PUT して URL が失効する。
関連概念
- serverless-stateless-execution — サーバを経由しない直接アップロードが無状態・短命な関数環境に適する理由。