js分片下載超出2G的大文件代碼實例
思路
針對超過2G的大文件,通常需要將文件進行分塊下載,以避免瀏覽器的內(nèi)存溢出或者因為網(wǎng)絡(luò)連接不穩(wěn)定而導(dǎo)致整個下載失敗的情況。
下面是一個基本的分塊下載的代碼樣例:
async downloadFile(url, fileName) { const CHUNK_SIZE = 1024 * 1024 * 10 // 每次下載10MB const response = await fetch(url) const contentRange = response.headers.get('content-range') const fileSize = contentRange ? Number(contentRange.split('/')[1]) : response.headers.get('content-length') const fileStream = [] let offset = 0 while (offset < fileSize) { const end = Math.min(offset + CHUNK_SIZE, fileSize) const options = { headers: { 'Range': `bytes=${offset}-${end - 1}` } } const blob = await fetch(url, options).then(res => res.blob()) fileStream.push(blob) offset = end } const blob = new Blob(fileStream, { type: response.headers.get('content-type') }) saveAs(blob, fileName) }
解釋
這段代碼使用了Fetch API來下載文件,同時使用了Range頭來告訴服務(wù)器只需要下載文件的一部分。將文件大小分割為塊,每次下載一塊數(shù)據(jù),最后將數(shù)據(jù)組合成一個Blob對象進行下載。需要注意的是,如果下載的文件有Content-Range頭,則需要先從這個頭里獲取文件總大小。
async downloadFile(url, fileName) {
- 這段代碼是一個異步函數(shù),用于從給定的URL下載大文件,并使用blob對象保存文件。
- 該函數(shù)需要傳入兩個參數(shù),一個是要下載的文件的url,另外一個則是下載后要保存的文件名。
const CHUNK_SIZE = 1024 * 1024 * 10 // 每次下載10MB
- 為了避免下載整個文件的時候因為內(nèi)存不足而導(dǎo)致的錯誤,把文件劃分成了多個大小相等的塊,每次下載一個塊的數(shù)據(jù)。這里設(shè)置每次下載的塊為10MB。
const response = await fetch(url) const contentRange = response.headers.get('content-range') const fileSize = contentRange ? Number(contentRange.split('/')[1]) : response.headers.get('content-length')
- 使用Fetch API向服務(wù)器請求數(shù)據(jù)。從響應(yīng)頭里獲取了Content-Range,這個頭可以告訴服務(wù)器只需要下載文件的一部分。如果不存在Content-Range頭,那么就通過response.headers.get(‘content-length’)獲取文件的總大小。
const fileStream = [] let offset = 0 while (offset < fileSize) { const end = Math.min(offset + CHUNK_SIZE, fileSize) const options = { headers: { 'Range': `bytes=${offset}-${end - 1}` } } const blob = await fetch(url, options).then(res => res.blob()) fileStream.push(blob) offset = end }
- 使用一個while循環(huán)將整個文件分塊下載,每次下載一塊數(shù)據(jù)。
- 循環(huán)中定義了兩個變量:一個是fileStream,用于保存從服務(wù)器下載的每個塊;另一個則是offset,表示已經(jīng)下載的數(shù)據(jù)大小。
- 在每次循環(huán)開始時,首先計算本次下載的起點和終點,然后構(gòu)造一個包含Range頭的選項對象。將起點和終點放在Range頭里,這樣服務(wù)器就只會返回指定范圍的數(shù)據(jù)。最后,使用Fetch API向服務(wù)器請求數(shù)據(jù),并將用于保存數(shù)據(jù)的Blob對象推入fileStream數(shù)組中。
- 最后,更新offset的值,表示已經(jīng)下載的總大小。
const blob = new Blob(fileStream, { type: response.headers.get('content-type') }) saveAs(blob, fileName)
- 在while循環(huán)結(jié)束后,將fileStream數(shù)組中的所有Blob對象合并為一個Blob對象,并使用FileSaver.js的saveAs函數(shù)來將其保存在本地磁盤上。
- 需要注意的是,在Blob的構(gòu)造函數(shù)中傳入了response.headers.get(‘content-type’),這是由于下載的文件類型不一定是常規(guī)的文件類型,所以我們需要從響應(yīng)頭中獲取正確的文件類型。
- 以上就是該函數(shù)的所有內(nèi)容,它通過將文件劃分為多個塊,避免了下載整個大文件時可能導(dǎo)致的內(nèi)存溢出等問題,并且沒有在瀏覽器中產(chǎn)生任何嚴重的內(nèi)存或性能問題。
附:javascript 大文件下載,分片下載,斷點續(xù)傳
// status const DONE = 4; // range size const RANGE_SIZE = 100; // get content length function getContentLength(url) { return new Promise((resolve, reject) => { var xhr = new XMLHttpRequest(); xhr.open('HEAD', url, true); xhr.onreadystatechange = function () { if (this.readyState == DONE) { resolve(this.getResponseHeader('Content-Length')); } } xhr.send(); }) } // get range content function getRangeContent(startIndex, endIndex, url) { return new Promise((resolve, reject) => { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.setRequestHeader('Range', `bytes=${startIndex}-${endIndex}`); xhr.responseType = 'arraybuffer'; xhr.onreadystatechange = function () { if (this.readyState == DONE) { console.log(this.response) resolve(this.response); } } xhr.send(); }) } // download arraybuffer file function downloadArrayBufferFile(arrayBuffer, fileName) { const blob = new Blob([arrayBuffer], { type: 'application/octet-stream' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = fileName; a.click(); } // concat arraybuffer array function concatArrayBuffer(arrayBufferArray) { let totalLength = 0; arrayBufferArray.forEach(arrayBuffer => { totalLength += arrayBuffer.byteLength; }); const result = new Uint8Array(totalLength); let offset = 0; arrayBufferArray.forEach(arrayBuffer => { result.set(new Uint8Array(arrayBuffer), offset); offset += arrayBuffer.byteLength; }); return result; } // main methoeds async function main() { const fileUrl = 'http://localhost:8083/public/access.txt'; const contentLength = await getContentLength(fileUrl); const numberRequest = Math.ceil(contentLength / RANGE_SIZE); const arrayBufferArray = []; for (let i = 0; i < numberRequest; i++) { const startIndex = i * RANGE_SIZE; const endIndex = startIndex + RANGE_SIZE - 1; const clip = await getRangeContent(startIndex, endIndex, fileUrl); arrayBufferArray.push(clip); } const result = concatArrayBuffer(arrayBufferArray); downloadArrayBufferFile(result, 'access.txt'); } main();
總結(jié)
到此這篇關(guān)于js分片下載超出2G大文件的文章就介紹到這了,更多相關(guān)js分片下載超2G大文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Bootstrap基本組件學(xué)習(xí)筆記之進度條(15)
這篇文章主要為大家詳細介紹了Bootstrap基本組件學(xué)習(xí)筆記之進度條,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12JavaScript利用切片實現(xiàn)大文件斷點續(xù)傳
斷點續(xù)傳即在文件上傳期間因為一些原因而導(dǎo)致上傳終止時,下次再次上傳同一個文件就從上一次上傳到一半的地方繼續(xù)上傳,以節(jié)省上傳時間。本文將利用切片實現(xiàn)大文件斷點續(xù)傳功能,快來跟隨小編一起學(xué)一學(xué)吧2022-03-03JavaScript30 一個月純 JS 挑戰(zhàn)中文指南(英文全集)
JavaScirpt30 是 Wes Bos 推出的一個 30 天挑戰(zhàn)。項目免費提供了 30 個視頻教程、30 個挑戰(zhàn)的起始文檔和 30 個挑戰(zhàn)解決方案源代碼。目的是幫助人們用純 JavaScript 來寫東西,不借助框架和庫,也不使用編譯器和引用2017-07-07D3.js實現(xiàn)簡潔實用的動態(tài)儀表盤的示例
本篇文章主要介紹了D3.js實現(xiàn)簡潔實用的動態(tài)儀表盤的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04Bootstrap模態(tài)對話框中顯示動態(tài)內(nèi)容的方法
今天小編就為大家分享一篇Bootstrap模態(tài)對話框中顯示動態(tài)內(nèi)容的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08用JS動態(tài)改變表單form里的action值屬性的兩種方法
下面小編就為大家?guī)硪黄肑S動態(tài)改變表單form里的action值屬性的兩種方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-05-05