仕組みから学ぶ Web
応用読了目安 15#アップロード#署名付きURL#ストリーム#検証#タイムアウト

ファイルのアップロード — サーバーを通すか、通さないか

動画の投稿を許したら、アプリのメモリが枯れて全機能が落ちた。1人が 800MB を送っただけだった。受け取り方を決める前に、どこを通すかを決める。

この記事の進み方

What — 経路は2つある

ファイルをどこに置くかは同じでも、どこを通すかで性質がまったく変わります。

Compare2つの経路

アプリがファイルを受け取り、検証してから保存先へ送る。

ブラウザ ──▶ アプリ ──▶ S3
            │
            └ 検証・変換ができる
検証のしやすさ
やりやすい
サーバーの負荷
全部通る
大きいファイル
苦しい
実装の単純さ
単純
  • 中身を見てから保存できる。ウイルス検査や変換を挟みやすい。
  • 帯域もメモリもアプリが負担する。冒頭の事故はここ。
  • ロードバランサやプロキシのタイムアウト・サイズ上限にも引っかかる。

小さいファイルならサーバー経由で十分。大きくなるほど、直接アップロードの利点が効いてくる。

署名付き URL
一定時間だけ有効な、特定の操作を許す URL。これを持つ人だけが保存先へ書ける。
マルチパートアップロード
大きなファイルを分割して送る仕組み。途中から再開できる。
ストリーム処理
全体をメモリに載せず、流れてくるぶんだけ順に扱うこと。
孤児ファイル
保存先には存在するが、アプリ側の記録と結びついていないファイル。
Content-Type
ファイルの種類を示す申告。ブラウザや利用者が自由に指定できるので、信用できない。

境目はおおむね「数十 MB」

どちらを選ぶかの目安です。

ファイルの大きさ経路
〜数 MB(画像、PDF)サーバー経由で十分
数十 MB〜(音声、資料)どちらでも。サーバーの規模次第
数百 MB〜(動画)直接アップロード一択

上限を「1GB」と決めた時点で、サーバー経由という選択肢は消えていました。 冒頭の事故は、上限だけ決めて経路を決め直さなかったことが原因です。

Why — サーバーを通すと、通り道のすべてが制約になる

途中の箱にも上限がある

ブラウザからアプリまでの間には、いくつもの箱があります。

Figureアップロードが通る箱と、それぞれの上限
  1. 1ブラウザ
  2. 2CDN / ロードバランサ
  3. 3アプリ
  4. 4保存先
1/4
ブラウザ大きなファイルでもメモリに全部載せずに送れる。ここは問題になりにくい

どれか1つでも引っかかれば失敗する。そしてエラーの出方が箱ごとに違うので、原因が分かりにくい。

箱ごとに独立した設定があり、片方だけ直しても通りません(network/load-balancer と同じ構造)。

メモリは「同時に何人が上げているか」で決まる

1人 800MB でメモリが枯れたのは、丸ごと読んでいたからです。

ストリームで流せば、一度に持つのは数 MB のバッファだけになります。しかし同時アップロード数が増えれば、それでも積み上がります。

1人あたり 8MB のバッファ × 同時 200 人 = 1.6GB

「1人ぶんが軽い」は「全体が軽い」ではありません。

直接アップロードには、直接アップロードの穴がある

サーバーを通さない代わりに、アプリが中身を知らないまま保存が完了します

  • 申告された種類と、実際の中身が違う
  • 完了を通知せず、ファイルだけ残る(孤児ファイル)
  • 上限を超えたファイルが置かれる

署名付き URL は「置いてよい」という許可であって、「何を置いてよいか」を細かく縛るものではありません。条件を付けられる範囲は保存先の仕組みに依存します。

演習 — まず自分で判断する

確認 — ここまで読めたか

上限 1GB の動画アップロードを実装します。経路はどちらを選びますか?

まず選ぶ(解答例は a〜d の記号で説明します)

確認 — ここまで読めたか

サーバー経由のアップロードで、メモリ不足を避けるには何をしますか?

まず選ぶ(解答例は a〜d の記号で説明します)

読み終わりましたか?

読了にすると、これを前提とする記事がロードマップで開放されます。