大きなファイルをアップロードする場合の解決策はすでにご存知の方もいらっしゃると思います。大きなファイルをアップロードする場合、アップロードの効率を上げるために、通常は Blob.slice メソッドを使用して、指定されたサイズに応じて大きなファイルを分割し、マルチスレッドを開始してブロックでアップロードします。すべてのブロックが正常にアップロードされたら、サーバーにブロックを結合するように通知します。 では、大きなファイルのダウンロードにも同様の考え方を採用できるでしょうか?サーバーが Range リクエスト ヘッダーをサポートしている場合は、次の図に示すように、マルチスレッド ブロック ダウンロードを実装することもできます。 上の図を読んだ後、大きなファイルをダウンロードするための解決策についてある程度理解できたと思います。次に、HTTP 範囲リクエストを紹介します。 1. HTTP範囲リクエストHTTP プロトコル範囲要求により、サーバーは HTTP メッセージの一部のみをクライアントに送信できます。範囲要求は、大きなメディア ファイルを転送する場合や、ファイル ダウンロードの再開機能と組み合わせて使用する場合に便利です。応答に Accept-Ranges ヘッダーが存在する場合 (その値が「none」ではない場合)、サーバーが範囲要求をサポートしていることを示します。 Range ヘッダーでは、一度に複数の部分を要求することができ、サーバーはそれらをマルチパート ファイルの形式で返します。サーバーが範囲応答を返す場合は、206 部分コンテンツ ステータス コードを使用する必要があります。要求された範囲が無効な場合、サーバーはクライアント エラーを示す 416 Range Not Satisfiable ステータス コードを返します。サーバーは Range ヘッダーを無視し、ステータス コード 200 でファイル全体を返すことができます。 1.1 範囲構文範囲: <単位>=<範囲開始>- 範囲: <単位>=<範囲開始>-<範囲終了> 範囲: <単位>=<範囲開始>-<範囲終了>、<範囲開始>-<範囲終了> 範囲: <単位>=<範囲開始>-<範囲終了>、<範囲開始>-<範囲終了>、<範囲開始>-<範囲終了>
Range 構文を理解した後、実際の使用例を見てみましょう。 1.1.1 単一カテゴリ $ curl http://i.imgur.com/z4d4kWk.jpg -i -H "範囲: バイト=0-1023" 1.1.2 複数の範囲 $ curl http://www.example.com -i -H "範囲: バイト=0-50、100-150" さて、HTTP 範囲リクエストに関する知識はこれですべてです。それでは、本題に入り、大きなファイルをダウンロードする方法を紹介していきましょう。 2. 大きなファイルをダウンロードする方法以下の内容をよりよく理解できるように、全体的なフローチャートを見てみましょう。 大きなファイルのダウンロードのプロセスを理解した後、まず上記のプロセスに関係するいくつかの補助関数を定義しましょう。 2.1 補助関数の定義2.1.1 getContentLength関数を定義する 名前が示すように、getContentLength 関数はファイルの長さを取得するために使用されます。この関数では、HEAD リクエストを送信し、応答ヘッダーから Content-Length 情報を読み取って、現在の URL に対応するファイルのコンテンツの長さを取得します。 関数 getContentLength(url) { 新しい Promise を返します ((resolve, reject) => { xhr = new XMLHttpRequest(); xhr.open("HEAD", url); xhr.send(); xhr.onload = 関数 () { 解決する( ~~xhr.getResponseHeader("コンテンツの長さ") ); }; xhr.onerror = 拒否; }); } 2.1.2 asyncPool 関数を定義する JavaScript で同時実行制御を実装するにはどうすればよいですか? この記事では、非同期タスクの同時実行制御を実装するために使用される asyncPool 関数を紹介しました。この関数は 3 つのパラメータを受け取ります。
非同期関数 asyncPool(poolLimit, 配列, iteratorFn) { const ret = []; // すべての非同期タスクを保存 const performing = []; // 実行中の非同期タスクを保存 for (const item of array) { // iteratorFn 関数を呼び出して非同期タスクを作成します const p = Promise.resolve().then(() => iteratorFn(item, array)); ret.push(p); // 新しい非同期タスクを保存 // poolLimit 値がタスクの総数以下の場合は、同時実行制御を実行します if (poolLimit <= array.length) { // タスクが完了したら、実行中のタスクの配列から完了したタスクを削除します。const e = p.then(() => execute.splice(executing.indexOf(e), 1)); 実行中の非同期タスクを保存する if (executing.length >= poolLimit) { await Promise.race(executing); // より高速なタスクが完了するまで待機します } } } Promise.all(ret) を返します。 } 2.1.3 getBinaryContent関数を定義する 関数 getBinaryContent(url, 開始, 終了, i) { 新しい Promise を返します ((resolve, reject) => { 試す { xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.setRequestHeader("range", `bytes=${start}-${end}`); // リクエスト ヘッダーに範囲リクエスト情報を設定します xhr.responseType = "arraybuffer"; // 戻り値の型を arraybuffer に設定します xhr.onload = 関数 () { 解決する({ index: i, // ファイル ブロックのインデックス buffer: xhr.response, // 範囲要求に対応するデータ }); }; xhr.send(); } キャッチ (エラー) { 拒否(新しいエラー(err)); } }); } ArrayBuffer オブジェクトは、汎用の固定長の生のバイナリ データ バッファーを表すために使用されることに注意してください。 ArrayBuffer の内容を直接操作することはできませんが、型付き配列オブジェクトまたは DataView オブジェクトを介して操作することができます。これらのオブジェクトは、バッファー内のデータを特定の形式で表し、これらの形式を使用してバッファーの内容を読み書きします。 2.1.4 連結関数を定義する ArrayBuffer オブジェクトを直接操作することはできないため、まず ArrayBuffer オブジェクトを Uint8Array オブジェクトに変換してから連結操作を実行する必要があります。以下に定義される連結関数は、ダウンロードされたファイル データ ブロックを結合するために使用されます。具体的なコードは次のとおりです。 関数連結(配列) { (!arrays.length) の場合は null を返します。 totalLength = arrays.reduce((acc, value) => acc + value.length, 0); とします。 結果を新しいUint8Array(totalLength)とします。 長さを 0 にします。 for (let 配列の配列) { 結果を設定します(配列、長さ); 長さ += 配列.長さ; } 結果を返します。 } 2.1.5 saveAs関数の定義 関数 saveAs({ name, buffers, mime = "application/octet-stream" }) { const blob = new Blob([バッファ], { type: mime }); const blobUrl = URL.createObjectURL(blob); 定数a = document.createElement("a"); a.download = 名前 || Math.random(); .href = blobUrl; クリック(); URL.revokeObjectURL(blob); } saveAs 関数では、Blob と Object URL を使用しました。オブジェクト URL は、Blob および File オブジェクトを画像、ダウンロード可能なバイナリ データ リンクなどの URL ソースとして使用できるようにする疑似プロトコルです。ブラウザでは、URL.createObjectURL メソッドを使用してオブジェクト URL を作成します。このメソッドは、Blob オブジェクトを受け取り、blob:<origin>/<uuid> の形式でそのオブジェクトに固有の URL を作成します。対応する例は次のとおりです。
ブラウザは、URL.createObjectURL によって生成された各 URL の URL → Blob マッピングを内部に保存します。したがって、このような URL は短くなりますが、BLOB にアクセスできます。生成された URL は、現在のドキュメントが開いている間のみ有効です。 さて、オブジェクト URL については以上です。 2.1.6 ダウンロード機能を定義する ダウンロード関数はダウンロード操作を実装するために使用され、次の 3 つのパラメータをサポートします。
非同期関数ダウンロード({ url, chunkSize, poolLimit = 1 }) { const contentLength = getContentLength(url); const chunks = typeof chunkSize === "number" ? Math.ceil(contentLength / chunkSize) : 1; const 結果 = asyncPool( を待機します プール制限、 [...新しい配列(チャンク).keys()], (i) => { start = i * chunkSize とします。 end = i + 1 == chunks とします。contentLength - 1 : (i + 1) * chunkSize - 1; getBinaryContent(url, start, end, i) を返します。 } ); const sortedBuffers = 結果 .map((item) => 新しい Uint8Array(item.buffer)); 連結した値を返します(sortedBuffers); } 2.2 大容量ファイルのダウンロードの使用例上記で定義した補助関数に基づいて、大きなファイルの並列ダウンロードを簡単に実装できます。具体的なコードは次のとおりです。 関数 multiThreadedDownload() { 定数 url = document.querySelector("#fileUrl").value; if (!url || !/https?/.test(url)) 戻り値: console.log("マルチスレッドダウンロードが開始されました: " + +new Date()); ダウンロード({ URL、 チャンクサイズ: 0.1 * 1024 * 1024, プール制限: 6, }).then((バッファ) => { console.log("マルチスレッドダウンロードが終了しました: " + +new Date()); saveAs({ buffers, name: "My compressed package", mime: "application/zip" }); }); } 完全なサンプルコードはかなり長いため、具体的なコードは掲載しません。ご興味がございましたら、以下のアドレスにアクセスしてサンプルコードを閲覧することができます。
ここでは、大きなファイルのダウンロード例の実行結果を見てみましょう。 結論この記事では、JavaScript の async-pool ライブラリが提供する asyncPool 関数を使用して、大きなファイルの並列ダウンロードを実現する方法を紹介します。 Abaoge 氏は、asyncPool 関数の紹介に加えて、HEAD リクエストを通じてファイル サイズを取得する方法、HTTP 範囲リクエストを開始する方法、クライアントにファイルを保存する方法などの関連知識も紹介しました。実際、asyncPool 関数は、大きなファイルの並列ダウンロードだけでなく、大きなファイルの並列アップロードも実現できます。興味のある方は、自分で試してみてください。 上記は、JavaScript で大容量ファイルの並列ダウンロードを実装する方法の詳細です。JavaScript で大容量ファイルの並列ダウンロードの詳細については、123WORDPRESS.COM の他の関連記事に注目してください。 以下もご興味があるかもしれません:
|
<<: MySQL 5.7.21 winx64 グリーンバージョンのインストールと設定方法のグラフィックチュートリアル
SQL の基礎知識がある友人は、「クロステーブル クエリ」について聞いたことがあるはずですが、クロス...
<br />XHTML と CSS がオブジェクト指向だったらよかったのに。 。太陽は北...
1. Concat関数。よく使用される接続文字列: concat 関数。たとえば、SQLクエリ条件...
問題の説明:構造:テストには2つのフィールドがあります。これらは col1 と col2 で、どちら...
1. 正規表現マッチング大文字と小文字を区別するマッチングの場合 ~ ~*は大文字と小文字を区別しな...
この記事の例では、検索ボックスでファジークエリを実装するためのNodeの具体的なコードを参考までに共...
現在、フロントエンドのパフォーマンス最適化について学んでいます。適切な解決策を見つけ、パフォーマンス...
順序なしリストでは、順序なしリストのシンボルは各リストの前に表示されるドットです。順序付きリスト o...
なぜ仮想DOMが必要なのでしょうか?仮想 DOM はブラウザのパフォーマンス問題を解決するために設計...
導入振り返ってみると、4年前、私がMySQLのインデックスについて学んでいたとき、先生はインデックス...
目次Vue.jsにおける属性とプロパティ値および関連する処理として属性とプロパティの概念属性とプロパ...
docker-compose でコンテナ ポートを公開する方法は、ports と expose の ...
日常業務では、次のようなレイアウトに遭遇することがあります。親要素のフレーム (ブラウザのサイズに応...
mysql コマンドを使用して MySQL サーバーに接続します。 MySQL サーバーが起動したら...
フロントエンド開発を行うと、PCとモバイル端末の適応に必然的に直面することになります。このような問題...