イベント写真アプリの開発者は、混雑した会場でもブラウザからのアップロードを維持するための具体的なチェックリストを手にしました。数十台のデバイスが同じホットスポットを奪い合う中、一人のユーザーがWi-Fiからモバイル通信へと切り替わることもあります。このガイドでは、ゲストがスマートフォンをロックしたり、ネットワークが一時的に不安定になったりしても、「アップロード完了」のトースト通知が表示された後に写真が消失してしまうのを防ぐ方法を解説します。
なぜ結婚式やフェスティバルでは通常のアップロードが失敗するのか
オフィスでは、ノートPCが安定したイーサネット接続に繋がっており、ユーザーが一度「送信」をクリックすれば済みます。しかし、結婚式の披露宴や音楽フェスティバルでは、同じ操作が連鎖的な問題を引き起こす可能性があります。ゲストが挙式会場から駐車場へと移動したり、数百台のスマートフォンによってルーターが悲鳴を上げたり、あるいはスマートフォンがWi-Fiを切断してモバイル通信に切り替わったりします。ブラウザはすべてのバイトをサーバーにストリーミングしたかもしれませんが、サーバー側ではまだファイルをストレージに書き込み(コミット)完了していません。プログレスバーが100%に達した瞬間にUIが成功を宣言してしまうと、ゲストが写真を削除してしまい、主催者の手元にはファイルが欠落した状態で残ってしまう可能性があります。
「ただアップロードするだけ」に潜むコスト
安易なアプローチでは、アップロードを単一のHTTP POSTとして扱います。接続が安定していれば機能しますが、混雑したネットワークでは、中断が発生するたびにファイル全体を最初からやり直すことになります。数十台のスマートフォンが同時にリトライを行うと、ユーザーのフラストレーションは募り、帯域幅のスパイクも発生します。ファイルをチャンク(塊)に分割して各ピースを追跡することは複雑さを増しますが、その見返りとして、ネットワークの切り替えが発生しても耐えられる、予測可能で低オーバーヘッドな転送が可能になります。
再開可能なチャンク分割アップロードシステムの構築
以下に、実践的なステップ・バイ・ステップの手順を示します。
1. データがブラウザを離れる前にアップロードIDを生成する
ローカルでユニバーサル一意識別子(UUID)を作成し、最初のリクエストとしてサーバーに送信します。サーバーはそのIDの下にセッションを記録します。後にタイムアウトなどでブラウザがリトライする場合、同じUUIDを含めることで、サーバーはセッションを認識し、重複エントリーを避けることができます。これにより、ワークフローがべき等(idempotent)になります。つまり、同じリクエストを繰り返しても悪影響を与えません。
2. ファイルを5〜10MBのチャンクに分割する
チャンクサイズはトレードオフの関係にあります。小さなチャンク(1MB未満)は、HTTPリクエストの数とそれに伴うヘッダーのオーバーヘッドを増加させます。逆にチャンクが大きすぎると、中断が発生した際のコストが高くなります。クライアントは大きな断片を再送しなければならないからです。一般的な写真や短い動画の場合、5〜10MBがバランスの取れた選択です。各リクエストがUIの応答性を維持できるほど迅速に完了しつつ、リクエスト数も管理可能な範囲に収まります。
3. 並列アップロードの数を制限する
モバイルブラウザは多くの接続を開くことができますが、混雑したWi-Fiネットワークでは、追加のストリームが増えるほど限られた帯域幅を奪い合うことになります。8つの競合するストリームよりも、2つの安定したストリームの方が優れています。navigator.connection APIを使用して低帯域幅の状態を検知し、並列実行数を自動的に減らすようにします。
4. IndexedDBにアップロード状態を保存する
アップロードID、送信済みのチャンクリスト、およびサーバーから承認されたオフセットを、ブラウザのIndexedDBに保存します。ページがリロードされたり、ユーザーがタブを閉じたりしても、クライアントは次回のロード時に状態を復元できます。ユーザーがページを再度開いた際には、同じファイルを選択するよう促します。保存されたメタデータにより、最初からやり直すのではなく、最後に確認されたチャンクからアップロードを再開できます。
5. navigator.onLine だけでなく、実際のネットワーク変化を検知する
navigator.onLine フラグは、接続が利用不可能な状態であっても「オンライン」と報告することがよくあります。代わりに、各チャンクに対して短いリクエストタイムアウト(例:5秒)を設定してください。タイムアウトが発生した場合は、ネットワークがダウンしているものとして扱います。接続が復旧した際は、サーバーに対して既に保持しているチャンクのリストを問い合わせ、不足しているピースのみをアップロードし続けます。これにより、一時的な通信断の後に重複したデータを送信することを避けられます。
6. リトライにはジッターを伴う指数バックオフを適用する
多くのゲストのデバイスがネットワークの復旧を検知した際、それらが一斉にリトライを開始するとサーバーが過負荷になります。指数バックオフ(exponential backoff)は、リトライのたびに待ち時間を前回のものより長く設定し、そこにジッター(jitter)としてランダムな小さなオフセットを加えます。この組み合わせにより、リトライのトラフィックが数秒間に分散され、急激なスパイクを防ぐことができます。
7. 重層的でアクセシブルなフィードバックを表示する
3段階のステータスバーによって、ファイルの実際の状態を伝えます:
- Received(受信済み) – サーバーがすべてのチャンクを保存し、ファイルを完了としてマークしました。
- Preparing(準備中) – サーバーがサムネイルの生成や動画のトランスコードを行っています。
- Available(利用可能) – 主催者がファイルを閲覧またはダウンロードできます。
色だけに頼るのを避け、アイコンに短いテキストを組み合わせることで、スクリーンリーダーのユーザーも進捗を理解できるようにしてください。
まだ起こりうる問題とは?
たとえ適切に設計されたレジューム可能なアップロードであっても、いくつかのエッジケースでつまずく可能性があります。
次に注目すべきこと
Webプラットフォームは進化し続けています。
まとめ
各パーツを追跡し、状態をローカルに保存し、インテリジェントにリトライを行う、レジューム可能なチャンクアップロードは、不安定なイベントネットワークをゲスト写真のための信頼できる経路へと変貌させます。上記のチェックリストを実装しましょう。
