プロンプトと適用範囲
ブラウザ上で大容量ファイルをアップロードし、ダイジェストを計算する必要があります。最新ブラウザはBlob.bytes()をサポートしていますが、古いブラウザはサポートしていない場合があります。読み込み、チャンク分割、フォールバック、キャンセル、エラーハンドリング、互換性の検証を設計してください。
MDNはBlob.bytes()をBaseline 2026としてマークしています。これはBlobデータを含むUint8Arrayに解決されるPromiseを返し、Web Workers内でも利用可能です。この面接ではAPIのセマンティクスとリソース境界がテストされます。新しいメソッドを単にサポートするだけでは、完全なアップロード設計とは言えません。
面接官が評価するポイント
- ストリームと呼ぶことなく、Promiseと
Uint8Arrayの結果を正確に説明できること。 - Blob全体を読み込むことによるメモリピークを認識し、チャンク分割またはストリームパスを設計できること。
- ブラウザ名チェックではなく、
arrayBuffer()またはstream()のフォールバックを伴う機能検出(feature detection)を使用していること。 - キャンセル、リトライ、ダイジェストの状態、Workerとのメッセージング、ユーザーへのフィードバックを処理できること。
- 互換性マトリックスと大容量ファイルのストレステストによって設計を検証できること。
確認すべき質問
- ファイルサイズ制限、アップロードの並行数、ターゲットブラウザ、Workerの利用可否はどのようになっているか?
- どのようなダイジェストアルゴリズム、サーバーチャンクプロトコル、再開セマンティクス、重複チャンクポリシーが適用されるか?
- アップロード前に完全なダイジェストが存在する必要があるか、それともクライアントが読み込みながらアップロードし最後に検証できるか?
- 失敗した転送を確認済みチャンクから再開できるか、またサーバーの冪等性はどのように定義されているか?
- ユーザーが別ページへ遷移したり、タブを閉じたり、デバイスにメモリ負荷がかかったりした場合はどうなるか?
APIセマンティクスと読み込みパス
Blob.bytes()は引数を取らず、Promiseを返します。満たされた(fulfilled)値はUint8Arrayとなり、読み込みに失敗するとPromiseは拒否(reject)されます。Blobの内容を1つのバイト配列として呼び出し元に渡すため、ゼロコピー動作や無制限の大容量ファイル対応が保証されるわけではありません。可能であれば小さなファイルはWorkerで読み込み、大容量ファイルの場合はslice()によるチャンク分割を行い、チャンクごとの読み込みとアップロードを優先します。
パスを機能検出します。利用可能な場合はbytes()を使用し、そうでない場合はarrayBuffer()を使用してUint8Arrayを作成します。ブラウザとプロトコルがサポートしている場合は、stream()を段階的に消費します。APIの差異によってサーバー側の検証が変化しないよう、フォールバック時でもチャンク番号、ダイジェスト入力、エラーコントラクトを維持する必要があります。
メモリ、チャンク、およびダイジェスト
Blob全体を読み込むと、ファイルサイズと同等以上のピークが発生し、さらにアップロードバッファ、デコード、ランタイムのオーバーヘッドが加わります。固定または適応的な境界でslice(start, end)を呼び出し、現在のチャンクと上限のあるアップロードキューのみを保持します。ファイル順にダイジェストへ渡し、並行アップロードによってダイジェスト入力の順序が乱れないようにします。
各チャンクはファイルID、バージョン、インデックス、長さ、コンテンツダイジェストを保持します。サーバーはファイルIDとインデックスによって冪等に保存し、その後順序どおりに結合して全体の長さと完全なファイルダイジェストを検証します。欠落、順序の入れ替わり、不正確な結合によって誤ったファイルが生成される可能性があるため、チャンクごとのダイジェストがエンドツーエンドのダイジェストの代わりになるわけではありません。
キャンセル、リトライ、およびWorker
読み込みおよびアップロードリクエストにAbortControllerシグナルを渡します。キャンセルによってキュー内のチャンクがクリアされ、参照が解放されます。指数バックオフと冪等なチャンクキーを使用して、回復可能なエラーのみをリトライします。認証の有効期限が切れた場合は、無限にリトライするのではなく、再認証のために一時停止します。Workerがダイジェストを計算する場合、バイト配列全体をコピーして戻すのではなく、メインスレッドは進捗、エラー、最終結果を受け取ります。
stream()を使用する場合は背圧(backpressure)を尊重し、リーダーがネットワークやダイジェストの消費速度を上回らないようにします。すべてのパスで、ファイルの内容や機密パスをログ出力することなく、確認済みチャンク、読み込みおよびアップロードの所要時間、リトライ、キャンセルの理由、推定ピークメモリを記録します。
互換性とセキュリティ境界
機能検出では、bytes()を選択する前にtypeof Blob !== "undefined"と"bytes" in Blob.prototypeを確認できます。User-Agentのみに依存しないでください。古いブラウザはarrayBuffer()または制御されたFileReaderにフォールバックでき、メモリバジェットを満たせない場合は明示的なメッセージを受け取る必要があります。
クライアントダイジェストは整合性の証拠を提供するものであり、認可、マルウェアスキャン、コンテンツタイプの検証を提供するものではありません。サーバーはファイルサイズ、チャンクサイズ、インデックス範囲、合計所要時間を制限する必要があります。クライアントのMIMEタイプはセキュリティ上の決定基準にはなりません。Worker間で機密データをコピーすることを避け、完了後は速やかに参照を解放してください。
障害訓練と検証チェックリスト
bytes()の有無、Workerおよびメインスレッドのパス、空ファイル、1チャンクおよび複数チャンクのファイル、超大容量ファイル、ネットワーク切断、キャンセルと再開、重複チャンク、順不同チャンク、ダイジェスト不一致を網羅するブラウザを対象とします。ストレステストでは、p95の読み取り時間、アップロードスループット、ピークメモリ、ロングタスク、失敗率、復旧時間を記録します。
bytes()が拒否(reject)された場合は、チャンクの状態を保持し、明示的なフォールバックに切り替えます。フォールバックがメモリバジェットを超える場合は、強制的に全体読み込みを行うのではなく、次のステップを示すメッセージを出して停止します。サーバーがチャンクまたは全体のダイジェスト不一致を検出した場合は、未確認の結合データを破棄し、最後に整合性が確認されたチャンクから再開します。
フォローアップの質問と模範回答
bytes()とarrayBuffer()をどのように使い分けますか?
どちらもBlob全体をメモリにロードする可能性があります。bytes()はバイト指向のコードに有用なUint8Arrayを直接提供しますが、自動的にストリーミングを提供するわけではありません。大容量ファイルにはチャンク分割またはストリームパスを使用する必要があります。
なぜクライアント側のみのファイル全体ダイジェストでは不十分なのですか?
サーバーがすべてのチャンクを順番に受信したことを証明できず、認可やコンテンツの安全性チェックの代わりにもならないためです。チャンクインデックス、チャンクダイジェスト、合計の長さ、およびサーバー側の最終ダイジェストを検証してください。
古いブラウザ向けのフォールバックをどのように検証しますか?
機能検出によって両方の機能パスを強制的に実行し、実ブラウザマトリックス、Workerおよびメインスレッドでの実行、メモリ負荷、オフライン復旧、キャンセルをテストします。単一の最新ブラウザだけでは不十分です。