js分片下載超出2G的大文件代碼實(shí)例
思路
針對超過2G的大文件,通常需要將文件進(jìn)行分塊下載,以避免瀏覽器的內(nèi)存溢出或者因?yàn)榫W(wǎng)絡(luò)連接不穩(wěn)定而導(dǎo)致整個(gè)下載失敗的情況。
下面是一個(gè)基本的分塊下載的代碼樣例:
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來下載文件,同時(shí)使用了Range頭來告訴服務(wù)器只需要下載文件的一部分。將文件大小分割為塊,每次下載一塊數(shù)據(jù),最后將數(shù)據(jù)組合成一個(gè)Blob對象進(jìn)行下載。需要注意的是,如果下載的文件有Content-Range頭,則需要先從這個(gè)頭里獲取文件總大小。
async downloadFile(url, fileName) {
- 這段代碼是一個(gè)異步函數(shù),用于從給定的URL下載大文件,并使用blob對象保存文件。
- 該函數(shù)需要傳入兩個(gè)參數(shù),一個(gè)是要下載的文件的url,另外一個(gè)則是下載后要保存的文件名。
const CHUNK_SIZE = 1024 * 1024 * 10 // 每次下載10MB
- 為了避免下載整個(gè)文件的時(shí)候因?yàn)閮?nèi)存不足而導(dǎo)致的錯(cuò)誤,把文件劃分成了多個(gè)大小相等的塊,每次下載一個(gè)塊的數(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,這個(gè)頭可以告訴服務(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 }
- 使用一個(gè)while循環(huán)將整個(gè)文件分塊下載,每次下載一塊數(shù)據(jù)。
- 循環(huán)中定義了兩個(gè)變量:一個(gè)是fileStream,用于保存從服務(wù)器下載的每個(gè)塊;另一個(gè)則是offset,表示已經(jīng)下載的數(shù)據(jù)大小。
- 在每次循環(huán)開始時(shí),首先計(jì)算本次下載的起點(diǎn)和終點(diǎn),然后構(gòu)造一個(gè)包含Range頭的選項(xiàng)對象。將起點(diǎn)和終點(diǎn)放在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對象合并為一個(gè)Blob對象,并使用FileSaver.js的saveAs函數(shù)來將其保存在本地磁盤上。
- 需要注意的是,在Blob的構(gòu)造函數(shù)中傳入了response.headers.get(‘content-type’),這是由于下載的文件類型不一定是常規(guī)的文件類型,所以我們需要從響應(yīng)頭中獲取正確的文件類型。
- 以上就是該函數(shù)的所有內(nèi)容,它通過將文件劃分為多個(gè)塊,避免了下載整個(gè)大文件時(shí)可能導(dǎo)致的內(nèi)存溢出等問題,并且沒有在瀏覽器中產(chǎn)生任何嚴(yán)重的內(nèi)存或性能問題。
附:javascript 大文件下載,分片下載,斷點(diǎn)續(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í)筆記之進(jìn)度條(15)
這篇文章主要為大家詳細(xì)介紹了Bootstrap基本組件學(xué)習(xí)筆記之進(jìn)度條,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12JavaScript利用切片實(shí)現(xiàn)大文件斷點(diǎn)續(xù)傳
斷點(diǎn)續(xù)傳即在文件上傳期間因?yàn)橐恍┰蚨鴮?dǎo)致上傳終止時(shí),下次再次上傳同一個(gè)文件就從上一次上傳到一半的地方繼續(xù)上傳,以節(jié)省上傳時(shí)間。本文將利用切片實(shí)現(xiàn)大文件斷點(diǎn)續(xù)傳功能,快來跟隨小編一起學(xué)一學(xué)吧2022-03-03JavaScript30 一個(gè)月純 JS 挑戰(zhàn)中文指南(英文全集)
JavaScirpt30 是 Wes Bos 推出的一個(gè) 30 天挑戰(zhàn)。項(xiàng)目免費(fèi)提供了 30 個(gè)視頻教程、30 個(gè)挑戰(zhàn)的起始文檔和 30 個(gè)挑戰(zhàn)解決方案源代碼。目的是幫助人們用純 JavaScript 來寫東西,不借助框架和庫,也不使用編譯器和引用2017-07-07用Webpack構(gòu)建Vue項(xiàng)目的實(shí)踐
這篇文章主要介紹了用Webpack構(gòu)建Vue項(xiàng)目的實(shí)踐,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11D3.js實(shí)現(xiàn)簡潔實(shí)用的動(dòng)態(tài)儀表盤的示例
本篇文章主要介紹了D3.js實(shí)現(xiàn)簡潔實(shí)用的動(dòng)態(tài)儀表盤的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04Bootstrap模態(tài)對話框中顯示動(dòng)態(tài)內(nèi)容的方法
今天小編就為大家分享一篇Bootstrap模態(tài)對話框中顯示動(dòng)態(tài)內(nèi)容的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08用JS動(dòng)態(tài)改變表單form里的action值屬性的兩種方法
下面小編就為大家?guī)硪黄肑S動(dòng)態(tài)改變表單form里的action值屬性的兩種方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-05-05