淺析前端常見文件下載的9種場景:Blob基礎知識/組成/Blob URL、a標簽下載、showSaveFilePicker API下載(兼容性差)、FileSaver.js庫下載、Zip下載(JSZip庫)、附件形式下載(設置Content-Disposition)、base64格式下載(需轉為blob)、分塊傳輸下載、HTTP范圍請求下載、大文件分塊並行下載


  一般在我們工作中,主要會涉及到 9 種文件下載的場景,每一種場景背后都使用不同的技術,其中也有很多細節需要我們額外注意。

  在瀏覽器端處理文件的時候,我們經常會用到 Blob 。比如圖片本地預覽、圖片壓縮、大文件分塊上傳及文件下載。在瀏覽器端文件下載的場景中,比如我們今天要講到的 a 標簽下載showSaveFilePicker API 下載Zip 下載 等場景中,都會使用到 Blob ,所以我們有必要在學習具體應用前,先掌握它的相關知識,這樣可以幫助我們更好地了解示例代碼。

一、基礎知識

1、什么是 Blob

  Blob(Binary Large Object)表示二進制類型的大對象。在數據庫管理系統中,將二進制數據存儲為一個單一個體的集合。Blob 通常是影像、聲音或多媒體文件。在 JavaScript 中 Blob 類型的對象表示一個不可變、原始數據的類文件對象。 它的數據可以按文本或二進制的格式進行讀取,也可以轉換成 ReadableStream 用於數據操作。

  Blob 對象由一個可選的字符串 type(通常是 MIME 類型)和 blobParts 組成:

  在 JavaScript 中你可以通過 Blob 的構造函數來創建 Blob 對象,Blob 構造函數的語法如下:

const aBlob = new Blob(blobParts, options);

  相關的參數說明如下:

(1)blobParts:它是一個由 ArrayBuffer,ArrayBufferView,Blob,DOMString 等對象構成的數組。DOMStrings 會被編碼為 UTF-8。

(2)options:一個可選的對象,包含以下兩個屬性:

  • type —— 默認值為 "",它代表了將會被放入到 blob 中的數組內容的 MIME 類型。
  • endings —— 默認值為 "transparent",用於指定包含行結束符\n 的字符串如何被寫入。 它是以下兩個值中的一個:"native",代表行結束符會被更改為適合宿主操作系統文件系統的換行符,或者 "transparent",代表會保持 blob 中保存的結束符不變。

2、了解 Blob URL

  Blob URL/Object URL 是一種偽協議,允許 Blob 和 File 對象用作圖像、下載二進制數據鏈接等的 URL 源。在瀏覽器中,我們使用 URL.createObjectURL 方法來創建 Blob URL,該方法接收一個 Blob 對象,並為其創建一個唯一的 URL,其形式為 blob:<origin>/<uuid>,對應的示例如下:blob:http://localhost:3000/53acc2b6-f47b-450f-a390-bf0665e04e59

  瀏覽器內部為每個通過 URL.createObjectURL 生成的 URL 存儲了一個URL → Blob 映射。因此,此類 URL 較短,但可以訪問 Blob。生成的 URL 僅在當前文檔打開的狀態下才有效。它允許引用 <img><a> 中的 Blob,但如果你訪問的 Blob URL 不再存在,則會從瀏覽器中收到 404 錯誤。

  上述的 Blob URL 看似很不錯,但實際上它也有副作用。 雖然存儲了 URL → Blob 的映射,但 Blob 本身仍駐留在內存中,瀏覽器無法釋放它。映射在文檔卸載時自動清除,因此 Blob 對象隨后被釋放。但是,如果應用程序壽命很長,那么 Blob 在短時間內將無法被瀏覽器釋放。因此,如果你創建一個 Blob URL,即使不再需要該 Blob,它也會存在內存中。

  針對這個問題,你可以調用 URL.revokeObjectURL(url) 方法,從內部映射中刪除引用,從而允許刪除 Blob(如果沒有其他引用),並釋放內存。

  現在你已經了解了 Blob 和 Blob URL,下面我們開始介紹客戶端文件下載的場景。隨着 Web 技術的不斷發展,瀏覽器的功能也越來越強大。這些年出現了很多在線 Web 設計工具,比如在線 PS、在線海報設計器或在線自定義表單設計器等。這些 Web 設計器允許用戶在完成設計之后,把生成的文件保存到本地,其中有一部分設計器就是利用瀏覽器提供的 Web API 來實現客戶端文件下載。

二、a 標簽下載

  需求:將 3 張圖片合成為 1 張圖片然后下載。整體的操作流程相對簡單:圖片合成 和 圖片下載

1、圖片合成

  圖片合成的功能可以直接使用 Github 上 merge-images 這個第三方庫來實現。利用該庫提供的 mergeImages(images, [options]) 方法,我們可以輕松地實現圖片合成的功能。調用該方法后,會返回一個 Promise 對象,當異步操作完成后,合成的圖片會以 Data URLs 的格式返回。

const mergePicEle = document.querySelector("#mergedPic"); const images = ["/body.png", "/eyes.png", "/mouth.png"].map( (path) => "../images" + path ); async function merge() { let imgDataUrl = await mergeImages(images); mergePicEle.src = imgDataUrl; }

2、圖片下載

  圖片下載的功能是借助 dataUrlToBlob 和 saveFile 這兩個函數來實現。它們分別用於實現 Data URLs => Blob 的轉換和文件的保存,具體的代碼如下所示:

function dataUrlToBlob(base64, mimeType) { let bytes = window.atob(base64.split(",")[1]); let ab = new ArrayBuffer(bytes.length); let ia = new Uint8Array(ab); for (let i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i); } return new Blob([ab], { type: mimeType }); } // 保存文件
function saveFile(blob, filename) { const a = document.createElement("a"); a.download = filename; a.href = URL.createObjectURL(blob); a.click(); URL.revokeObjectURL(a.href) }

  當設置好 a 元素的 download 屬性之后,我們會調用 URL.createObjectURL 方法來創建 Object URL,並把返回的 URL 賦值給 a 元素的 href屬性。接着通過調用 a 元素的 click 方法來觸發文件的下載操作,最后還會調用一次 URL.revokeObjectURL 方法,從內部映射中刪除引用,從而允許刪除 Blob(如果沒有其他引用),並釋放內存。

  關於 a 標簽下載 的內容就介紹到這,下面我們來介紹如何使用新的 Web API —— showSaveFilePicker 實現文件下載。

三、showSaveFilePicker API 下載

  showSaveFilePicker API 是 Window 接口中定義的方法,調用該方法后會顯示允許用戶選擇保存路徑的文件選擇器。該方法如下所示:

let FileSystemFileHandle = Window.showSaveFilePicker(options);

  showSaveFilePicker 方法支持一個對象類型的可選參數,可包含以下屬性:

(1)excludeAcceptAllOption:布爾類型,默認值為 false

  默認情況下,選擇器應包含一個不應用任何文件類型過濾器的選項(由下面的types 選項啟用)。將此選項設置為 true 意味着 types 選項不可用。

(2)types:數組類型,表示允許保存的文件類型列表。數組中的每一項是包含以下屬性的配置對象:

  • description(可選):用於描述允許保存文件類型類別。
  • accept:是一個對象,該對象的 key 是 MIME 類型,值是文件擴展名列表。

  調用 showSaveFilePicker 方法之后,會返回一個 FileSystemFileHandle 對象。有了該對象,你就可以調用該對象上的方法來操作文件。比如調用該對象上的 createWritable 方法之后,就會返回 FileSystemWritableFileStream 對象,就可以把數據寫入到文件中。具體的使用方式如下所示:

async function saveFile(blob, filename) { try { const handle = await window.showSaveFilePicker({ suggestedName: filename, types: [ { description: "PNG file", accept: { "image/png": [".png"], }, }, { description: "Jpeg file", accept: { "image/jpeg": [".jpeg"], }, }, ], }); const writable = await handle.createWritable(); await writable.write(blob); await writable.close(); return handle; } catch (err) { console.error(err.name, err.message); } } function download() { if (!imgDataUrl) { alert("請先合成圖片"); return; } const imgBlob = dataUrlToBlob(imgDataUrl, "image/png"); saveFile(imgBlob, "face.png"); }

  當你使用以上更新后的 saveFile 函數,來保存已合成的圖片時,會顯示以下保存文件選擇器:

  由上圖可知,相比 a 標簽下載 的方式,showSaveFilePicker API 允許你選擇文件的下載目錄、選擇文件的保存格式和更改存儲的文件名稱。看到這里是不是覺得 showSaveFilePicker API 功能挺強大的,不過可惜的是該 API 目前的兼容性還不是很好。

  其實 showSaveFilePicker 是 File System Access API 中定義的方法,除了 showSaveFilePicker 之外,還有 showOpenFilePicker 和 showDirectoryPicker 等方法。如果你想在實際項目中使用這些 API 的話,可以考慮使用 GoogleChromeLabs 開源的 browser-fs-access 這個庫,該庫可以讓你在支持平台上更方便地使用 File System Access API,對於不支持的平台會自動降級使用 <input type="file"> 和 <a download> 的方式。

  可能大家對 browser-fs-access 這個庫會比較陌生,但是如果換成是 FileSaver.js  這個庫的話,應該就比較熟悉了。接下來,我們來介紹如何利用 FileSaver.js 這個庫實現客戶端文件下載。

四、FileSaver 下載

  FileSaver.js 是在客戶端保存文件的解決方案,非常適合在客戶端上生成文件的 Web 應用程序。它是 HTML5 版本的 saveAs() FileSaver 實現,支持大多數主流的瀏覽器。在引入 FileSaver.js 這個庫之后,我們就可以使用它提供的 saveAs 方法來保存文件。該方法對應的簽名如下所示:

FileSaver saveAs( Blob/File/Url, optional DOMString filename, optional Object { autoBom } )

  saveAs 方法支持 3 個參數,第 1 個參數表示它支持 Blob/File/Url 三種類型,第 2 個參數表示文件名(可選),而第 3 個參數表示配置對象(可選)。如果你需要 FlieSaver.js 自動提供 Unicode 文本編碼提示(參考:字節順序標記),則需要設置 { autoBom: true}

  了解完 saveAs 方法之后,我們來舉 3 個具體的使用示例:

1、保存文本

let blob = new Blob(["大家好,我是***"], { type: "text/plain;charset=utf-8" }); saveAs(blob, "hello.txt");

2、保存線上資源

saveAs("https://httpbin.org/image", "image.jpg");

  如果下載的 URL 地址與當前站點是同域的,則將使用 a[download] 方式下載。否則,會先使用 同步的 HEAD 請求 來判斷是否支持 CORS 機制,若支持的話,將進行數據下載並使用 Blob URL 實現文件下載。如果不支持 CORS 機制的話,將會嘗試使用 a[download] 方式下載。

3、保存 canvas 畫布內容

let canvas = document.getElementById("my-canvas"); canvas.toBlob(function(blob) { saveAs(blob, "abao.png"); });

  需要注意的是 canvas.toBlob() 方法並非在所有瀏覽器中都可用,對於這個問題,你可以考慮使用 canvas-toBlob.js 來解決兼容性問題。

  介紹完 saveAs 方法的使用示例之后,我們來更新前面示例中的 download 方法:

function download() { if (!imgDataUrl) { alert("請先合成圖片"); return; } const imgBlob = dataUrlToBlob(imgDataUrl, "image/png"); saveAs(imgBlob, "face.png"); }

  很明顯,使用 saveAs 方法之后,下載已合成的圖片就很簡單了。如果你對 FileSaver.js 的工作原理感興趣的話,可以閱讀 聊一聊 15.5K 的 FileSaver,是如何工作的? 這篇文章。前面介紹的場景都是直接下載單個文件,其實我們也可以在客戶端同時下載多個文件,然后把已下載的文件壓縮成 Zip 包並下載到本地。

五、Zip 下載

  利用 JSZip 這個庫提供的 API,把待上傳目錄下的所有文件壓縮成 ZIP 文件,然后再把生成的 ZIP 文件上傳到服務器。同樣利用 JSZip 這個庫,我們可以實現在客戶端同時下載多個文件,然后把已下載的文件壓縮成 Zip 包,並下載到本地的功能。

const images = ["body.png", "eyes.png", "mouth.png"]; const imageUrls = images.map((name) => "../images/" + name); async function download() { let zip = new JSZip(); Promise.all(imageUrls.map(getFileContent)).then((contents) => { contents.forEach((content, i) => { zip.file(images[i], content); }); zip.generateAsync({ type: "blob" }).then(function (blob) { saveAs(blob, "material.zip"); }); }); } // 從指定的url上下載文件內容
function getFileContent(fileUrl) { return new JSZip.external.Promise(function (resolve, reject) { // 調用jszip-utils庫提供的getBinaryContent方法獲取文件內容
 JSZipUtils.getBinaryContent(fileUrl, function (err, data) { if (err) { reject(err); } else { resolve(data); } }); }); }

  在以上代碼中,當用戶點擊 打包下載 按鈕時,就會調用 download 函數。在該函數內部,會先調用 JSZip 構造函數創建 JSZip 對象,然后使用 Promise.all 函數來確保所有的文件都下載完成后,再調用 file(name, data [,options]) 方法,把已下載的文件添加到前面創建的JSZip 對象中。最后通過 zip.generateAsync 函數來生成 Zip 文件並使用 FileSaver.js 提供的 saveAs 方法保存 Zip 文件。

六、附件形式下載

  在服務端下載的場景中,附件形式下載是一種比較常見的場景。在該場景下,我們通過設置 Content-Disposition 響應頭來指示響應的內容以何種形式展示,是以內聯(inline)的形式,還是以附件(attachment)的形式下載並保存到本地。

Content-Disposition: inline Content-Disposition: attachment Content-Disposition: attachment; filename="mouth.png"

  而在 HTTP 表單的場景下, Content-Disposition 也可以作為multipart body 中的消息頭:

Content-Disposition: form-data Content-Disposition: form-data; name="fieldName" Content-Disposition: form-data; name="fieldName"; filename="filename.jpg"

  第 1 個參數總是固定不變的 form-data;附加的參數不區分大小寫,並且擁有參數值,參數名與參數值用等號(=)連接,參數值用雙引號括起來。參數之間用分號(;)分隔。

  了解完 Content-Disposition 的作用之后,我們來看一下如何實現以附件形式下載的功能。Koa 是一個簡單易用的 Web 框架,它的特點是優雅、簡潔、輕量、自由度高。所以我們選擇它來搭建文件服務,並使用 @koa/router 中間件來處理路由:

// attachment/file-server.js
const fs = require("fs"); const path = require("path"); const Koa = require("koa"); const Router = require("@koa/router"); const app = new Koa(); const router = new Router(); const PORT = 3000; const STATIC_PATH = path.join(__dirname, "./static/"); // http://localhost:3000/file?filename=mouth.png
router.get("/file", async (ctx, next) => { const { filename } = ctx.query; const filePath = STATIC_PATH + filename; const fStats = fs.statSync(filePath); ctx.set({ "Content-Type": "application/octet-stream", "Content-Disposition": `attachment; filename=${filename}`, "Content-Length": fStats.size, }); ctx.body = fs.createReadStream(filePath); }); // 注冊中間件
app.use(async (ctx, next) => { try { await next(); } catch (error) { // ENOENT(無此文件或目錄):通常是由文件操作引起的,這表明在給定的路徑上無法找到任何文件或目錄
    ctx.status = error.code === "ENOENT" ? 404 : 500; ctx.body = error.code === "ENOENT" ? "文件不存在" : "服務器開小差"; } }); app.use(router.routes()).use(router.allowedMethods()); app.listen(PORT, () => { console.log(`應用已經啟動:http://localhost:${PORT}/`);
});

  以上的代碼被保存在 attachment 目錄下的 file-server.js 文件中,該目錄下還有一個 static 子目錄用於存放靜態資源。目前 static 目錄下包含以下 3 個 png 文件。

├── file-server.js └── static ├── body.png ├── eyes.png └── mouth.png

  當你運行 node file-server.js 命令成功啟動文件服務器之后,就可以通過正確的 URL 地址來下載 static 目錄下的文件。比如在瀏覽器中打開 http://localhost:3000/file?filename=mouth.png 這個地址,你就會開始下載 mouth.png 文件。而如果指定的文件不存在的話,就會返回文件不存在。

  在編寫 HTML 網頁時,對於一些簡單圖片,通常會選擇將圖片內容直接內嵌在網頁中,從而減少不必要的網絡請求,但是圖片數據是二進制數據,該怎么嵌入呢?絕大多數現代瀏覽器都支持一種名為 Data URLs 的特性,允許使用 Base64 對圖片或其他文件的二進制數據進行編碼,將其作為文本字符串嵌入網頁中。所以文件也可以通過 Base64 的格式進行傳輸,接下來我們將介紹如何下載 Base64 格式的圖片。

七、base64 格式下載

  因為是 base64 格式的圖片,所以在調用 FileSaver 提供的 saveAs 方法前,我們需要將 base64 字符串轉換成 blob 對象,該轉換是通過以下的 base64ToBlob 函數來完成,該函數的具體實現如下所示:

function base64ToBlob(base64, mimeType) { let bytes = window.atob(base64); let ab = new ArrayBuffer(bytes.length); let ia = new Uint8Array(ab); for (let i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i); } return new Blob([ab], { type: mimeType }); }

八、chunked 下載

  分塊傳輸編碼主要應用於如下場景,即要傳輸大量的數據,但是在請求在沒有被處理完之前響應的長度是無法獲得的。例如,當需要用從數據庫中查詢獲得的數據生成一個大的 HTML 表格的時候,或者需要傳輸大量的圖片的時候。

  要使用分塊傳輸編碼,則需要在響應頭配置 Transfer-Encoding 字段,並設置它的值為 chunked 或 gzip, chunked

Transfer-Encoding: chunked Transfer-Encoding: gzip, chunked

  響應頭 Transfer-Encoding 字段的值為 chunked,表示數據以一系列分塊的形式進行發送。需要注意的是 Transfer-Encoding 和 Content-Length 這兩個字段是互斥的,也就是說響應報文中這兩個字段不能同時出現。下面我們來看一下分塊傳輸的編碼規則:

  • 每個分塊包含分塊長度和數據塊兩個部分;
  • 分塊長度使用 16 進制數字表示,以 \r\n 結尾;
  • 數據塊緊跟在分塊長度后面,也使用 \r\n 結尾,但數據不包含 \r\n
  • 終止塊是一個常規的分塊,表示塊的結束。不同之處在於其長度為 0,即 0\r\n\r\n

  了解完分塊傳輸的編碼規則,我們來看如何利用分塊傳輸編碼實現文件下載。

const chunkedUrl = "http://localhost:3000/file?filename=file.txt"; function download() { return fetch(chunkedUrl) .then(processChunkedResponse) .then(onChunkedResponseComplete) .catch(onChunkedResponseError); } function processChunkedResponse(response) { let text = ""; let reader = response.body.getReader(); let decoder = new TextDecoder(); return readChunk(); function readChunk() { return reader.read().then(appendChunks); } function appendChunks(result) { let chunk = decoder.decode(result.value || new Uint8Array(), { stream: !result.done, }); console.log("已接收到的數據:", chunk); console.log("本次已成功接收", chunk.length, "bytes"); text += chunk; console.log("目前為止共接收", text.length, "bytes\n"); if (result.done) { return text; } else { return readChunk(); } } } function onChunkedResponseComplete(result) { let blob = new Blob([result], { type: "text/plain;charset=utf-8", }); saveAs(blob, "hello.txt"); } function onChunkedResponseError(err) { console.error(err); }

  當用戶點擊下載按鈕時,就會調用以上代碼中的 download 函數。在該函數內部,我們會使用 Fetch API 來執行下載操作。因為服務端的數據是以一系列分塊的形式進行發送,所以在瀏覽器端我們是通過流的形式進行接收。即通過 response.body 獲取可讀的 ReadableStream,然后用 ReadableStream.getReader() 創建一個讀取器,最后調用 reader.read 方法來讀取已返回的分塊數據。

  因為 file.txt 文件的內容是普通文本,且 result.value 的值是 Uint8Array 類型的數據,所以在處理返回的分塊數據時,我們使用了 TextDecoder 文本解碼器。一個解碼器只支持一種特定文本編碼,例如utf-8iso-8859-2koi8cp1261gbk 等等。

  如果收到的分塊非 終止塊result.done 的值是 false,則會繼續調用 readChunk 方法來讀取分塊數據。而當接收到 終止塊 之后,表示分塊數據已傳輸完成。此時,result.done 屬性就會返回 true。從而會自動調用 onChunkedResponseComplete 函數,在該函數內部,我們以解碼后的文本作為參數來創建 Blob 對象。之后,繼續使用 FileSaver 庫提供的 saveAs 方法實現文件下載。

  這里我們用 Wireshark 網絡包分析工具,抓了個數據包。具體如下圖所示:

  從圖中我們可以清楚地看到在 HTTP chunked response 下面包含了Data chunk(數據塊) 和 End of chunked encoding(終止塊)

  現在我們已經知道可以利用分塊傳輸編碼(Transfer-Encoding)實現數據的分塊傳輸,那么有沒有辦法獲取指定范圍內的文件數據呢?對於這個問題,我們可以利用 HTTP 協議的范圍請求。接下來,我們將介紹如何利用 HTTP 范圍請求來下載指定范圍的數據

九、范圍下載

  HTTP 協議范圍請求允許服務器只發送 HTTP 消息的一部分到客戶端。范圍請求在傳送大的媒體文件,或者與文件下載的斷點續傳功能搭配使用時非常有用。如果在響應中存在 Accept-Ranges 首部(並且它的值不為 “none”),那么表示該服務器支持范圍請求。

  在一個 Range 首部中,可以一次性請求多個部分,服務器會以 multipart 文件的形式將其返回。如果服務器返回的是范圍響應,需要使用 206 Partial Content 狀態碼。假如所請求的范圍不合法,那么服務器會返回 416 Range Not Satisfiable 狀態碼,表示客戶端錯誤。服務器允許忽略 Range 首部,從而返回整個文件,狀態碼用 200 。

// Range 語法:
Range: <unit>=<range-start>- Range: <unit>=<range-start>-<range-end> Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end> Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>, <range-start>-<range-end>
// unit:范圍請求所采用的單位,通常是字節(bytes)。 // <range-start>:一個整數,表示在特定單位下,范圍的起始值。 // <range-end>:一個整數,表示在特定單位下,范圍的結束值。這個值是可選的,如果不存在,表示此范圍一直延伸到文檔結束
// 單一范圍
$ curl http://i.imgur.com/z4d4kWk.jpg -i -H "Range: bytes=0-1023" // 多重范圍
$ curl http://www.example.com -i -H "Range: bytes=0-50, 100-150"

  了解完 Range 語法之后,我們來看一下實際的使用示例:

async function download() { try { let rangeContent = await getBinaryContent( "http://localhost:3000/file.txt", 0, 100, "text" ); const blob = new Blob([rangeContent], { type: "text/plain;charset=utf-8", }); saveAs(blob, "hello.txt"); } catch (error) { console.error(error); } } function getBinaryContent(url, start, end, responseType = "arraybuffer") { return new Promise((resolve, reject) => { try { let xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.setRequestHeader("range", `bytes=${start}-${end}`); xhr.responseType = responseType; xhr.onload = function () { resolve(xhr.response); }; xhr.send(); } catch (err) { reject(new Error(err)); } }); }

  當用戶點擊下載按鈕時,就會調用 download 函數。在該函數內部會通過調用 getBinaryContent 函數來發起范圍請求。對應的 HTTP 請求報文如下所示:

GET /file.txt HTTP/1.1 Host: localhost:3000 Connection: keep-alive User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36 Accept: */* Accept-Encoding: identity Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,id;q=0.7 Range: bytes=0-100

  而當服務器接收到該范圍請求之后,會返回對應的 HTTP 響應報文:

HTTP/1.1 206 Partial Content Vary: Origin Access-Control-Allow-Origin: null Accept-Ranges: bytes Last-Modified: Fri, 09 Jul 2021 00:17:00 GMT Cache-Control: max-age=0 Content-Type: text/plain; charset=utf-8 Date: Sat, 10 Jul 2021 02:19:39 GMT Connection: keep-alive Content-Range: bytes 0-100/2590 Content-Length: 101

  從以上的 HTTP 響應報文中,我們見到了前面介紹的 206 狀態碼和Accept-Ranges 首部。此外,通過 Content-Range 首部,我們就知道了文件的總大小。在成功獲取到范圍請求的響應體之后,我們就可以使用返回的內容作為參數,調用 Blob 構造函數創建對應的 Blob 對象,進而使用 FileSaver 庫提供的 saveAs 方法來下載文件了。

十、大文件分塊下載

  我們經常使用大文件分片上傳的解決方案:在上傳大文件時,為了提高上傳的效率,我們一般會使用 Blob.slice 方法對大文件按照指定的大小進行切割,然后在進行並行分塊上傳,等所有分塊都成功上傳后,再通知服務端進行分塊合並。

  那么對大文件下載來說,我們能否采用類似的思想呢?其實在服務端支持 Range 請求首部的條件下,我們也是可以實現大文件分塊下載的功能,具體處理方案如下圖所示:

  大文件並發下載的完整流程:

  詳細流程見這篇文章:《JavaScript 中如何實現大文件並行下載?》- https://mp.weixin.qq.com/s/E4SdYEkEzurfrnJrBu3bjA

原文鏈接:https://mp.weixin.qq.com/s/7Cz4n-9OY8DgMRNSKV-R9g


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM