您好,登錄后才能下訂單哦!
這篇文章主要介紹“JavaScript怎么實現(xiàn)大文件分片上傳處理”,在日常操作中,相信很多人在JavaScript怎么實現(xiàn)大文件分片上傳處理問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”JavaScript怎么實現(xiàn)大文件分片上傳處理”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
很多時候我們在處理文件上傳時,如視頻文件,小則幾十M,大則 1G+,以一般的HTTP請求發(fā)送數(shù)據(jù)的方式的話,會遇到的問題:
1、文件過大,超出服務(wù)端的請求大小限制;
2、請求時間過長,請求超時;
3、傳輸中斷,必須重新上傳導(dǎo)致前功盡棄
這些問題很影響用戶的體驗感,所以下面介紹一種基于原生JavaScript進(jìn)行文件分片處理上傳的方案,具體實現(xiàn)過程如下:
1、通過dom獲取文件對象,并且對文件進(jìn)行MD5加密(文件內(nèi)容+文件標(biāo)題形式),采用SparkMD5進(jìn)行文件加密;
2、進(jìn)行分片設(shè)置,文件File基于Blob, 繼承了Blob的功能,可以把File當(dāng)成Blob的子類,利于Blob的slice方法進(jìn)行文件分片處理,并且依次進(jìn)行上傳
3、分片文件上傳完成后,請求合并接口后端進(jìn)行文件合并處理即可
1. 上傳文件頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>文件上傳</title> <script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.js"></script> <style> /* 自定義進(jìn)度條樣式 */ .precent input[type=range] { -webkit-appearance: none; /*清除系統(tǒng)默認(rèn)樣式*/ width: 7.8rem; /* background: -webkit-linear-gradient(#ddd, #ddd) no-repeat, #ddd; */ /*設(shè)置左邊顏色為#61bd12,右邊顏色為#ddd*/ background-size: 75% 100%; /*設(shè)置左右寬度比例*/ height: 0.6rem; /*橫條的高度*/ border-radius: 0.4rem; border: 1px solid #ddd; box-shadow: 0 0 10px rgba(0,0,0,.125) inset ; } /*拖動塊的樣式*/ .precent input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; /*清除系統(tǒng)默認(rèn)樣式*/ height: .9rem; /*拖動塊高度*/ width: .9rem; /*拖動塊寬度*/ background: #fff; /*拖動塊背景*/ border-radius: 50%; /*外觀設(shè)置為圓形*/ border: solid 1px #ddd; /*設(shè)置邊框*/ } </style> </head> <body> <h2>大文件分片上傳測試</h2> <div> <input id="file" type="file" name="avatar" /> <div > <input id="submitBtn" type="button" value="提交" /> <input id="pauseBtn" type="button" value="暫停" /> </div> <div class="precent"> <input type="range" value="0" /><span id="precentVal">0%</span> </div> </div> <script type="text/javascript" src="./js/index.js"></script> </body> </html>
2. 大文件分片上傳處理
$(document).ready(() => { const submitBtn = $('#submitBtn'); //提交按鈕 const precentDom = $(".precent input")[0]; // 進(jìn)度條 const precentVal = $("#precentVal"); // 進(jìn)度條值對應(yīng)dom const pauseBtn = $('#pauseBtn'); // 暫停按鈕 // 每個chunk的大小,設(shè)置為1兆 const chunkSize = 1 * 1024 * 1024; // 獲取slice方法,做兼容處理 const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; // 對文件進(jìn)行MD5加密(文件內(nèi)容+文件標(biāo)題形式) const hashFile = (file) => { return new Promise((resolve, reject) => { const chunks = Math.ceil(file.size / chunkSize); let currentChunk = 0; const spark = new SparkMD5.ArrayBuffer(); const fileReader = new FileReader(); function loadNext() { const start = currentChunk * chunkSize; const end = start + chunkSize >= file.size ? file.size : start + chunkSize; fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); } fileReader.onload = e => { spark.append(e.target.result); // Append array buffer currentChunk += 1; if (currentChunk < chunks) { loadNext(); } else { console.log('finished loading'); const result = spark.end(); // 通過內(nèi)容和文件名稱進(jìn)行md5加密 const sparkMd5 = new SparkMD5(); sparkMd5.append(result); sparkMd5.append(file.name); const hexHash = sparkMd5.end(); resolve(hexHash); } }; fileReader.onerror = () => { console.warn('文件讀取失敗!'); }; loadNext(); }).catch(err => { console.log(err); }); } // 提交 submitBtn.on('click', async () => { var pauseStatus = false; var nowUploadNums = 0 // 1.讀取文件 const fileDom = $('#file')[0]; const files = fileDom.files; const file = files[0]; if (!file) { alert('沒有獲取文件'); return; } // 2.設(shè)置分片參數(shù)屬性、獲取文件MD5值 const hash = await hashFile(file); //文件 hash const blockCount = Math.ceil(file.size / chunkSize); // 分片總數(shù) const axiosPromiseArray = []; // axiosPromise數(shù)組 // 文件上傳 const uploadFile = () => { const start = nowUploadNums * chunkSize; const end = Math.min(file.size, start + chunkSize); // 構(gòu)建表單 const form = new FormData(); // blobSlice.call(file, start, end)方法是用于進(jìn)行文件分片 form.append('file', blobSlice.call(file, start, end)); form.append('index', nowUploadNums); form.append('hash', hash); // ajax提交 分片,此時 content-type 為 multipart/form-data const axiosOptions = { onUploadProgress: e => { nowUploadNums++; // 判斷分片是否上傳完成 if (nowUploadNums < blockCount) { setPrecent(nowUploadNums, blockCount); uploadFile(nowUploadNums) } else { // 4.所有分片上傳后,請求合并分片文件 axios.all(axiosPromiseArray).then(() => { setPrecent(blockCount, blockCount); // 全部上傳完成 axios.post('/file/merge_chunks', { name: file.name, total: blockCount, hash }).then(res => { console.log(res.data, file); pauseStatus = false; alert('上傳成功'); }).catch(err => { console.log(err); }); }); } }, }; // 加入到 Promise 數(shù)組中 if (!pauseStatus) { axiosPromiseArray.push(axios.post('/file/upload', form, axiosOptions)); } } // 設(shè)置進(jìn)度條 function setPrecent(now, total) { var prencentValue = ((now / total) * 100).toFixed(2) precentDom.value = prencentValue precentVal.text(prencentValue + '%') precentDom.style.cssText = `background:-webkit-linear-gradient(top, #059CFA, #059CFA) 0% 0% / ${prencentValue}% 100% no-repeat` } // 暫停 pauseBtn.on('click', (e) => { pauseStatus = !pauseStatus; e.currentTarget.value = pauseStatus ? '開始' : '暫停' if (!pauseStatus) { uploadFile(nowUploadNums) } }) uploadFile(); }); })
3. 文件上傳和合并分片文件接口(node)
const Router = require('koa-router'); const multer = require('koa-multer'); const fs = require('fs-extra'); const path = require('path'); const router = new Router(); const { mkdirsSync } = require('../utils/dir'); const uploadPath = path.join(__dirname, 'upload'); const chunkUploadPath = path.join(uploadPath, 'temp'); const upload = multer({ dest: chunkUploadPath }); // 文件上傳接口 router.post('/file/upload', upload.single('file'), async (ctx, next) => { const { index, hash } = ctx.req.body; const chunksPath = path.join(chunkUploadPath, hash, '/'); if(!fs.existsSync(chunksPath)) mkdirsSync(chunksPath); fs.renameSync(ctx.req.file.path, chunksPath + hash + '-' + index); ctx.status = 200; ctx.res.end('Success'); }) // 合并分片文件接口 router.post('/file/merge_chunks', async (ctx, next) => { const { name, total, hash } = ctx.request.body; const chunksPath = path.join(chunkUploadPath, hash, '/'); const filePath = path.join(uploadPath, name); // 讀取所有的chunks const chunks = fs.readdirSync(chunksPath); // 創(chuàng)建存儲文件 fs.writeFileSync(filePath, ''); if(chunks.length !== total || chunks.length === 0) { ctx.status = 200; ctx.res.end('切片文件數(shù)量不符合'); return; } for (let i = 0; i < total; i++) { // 追加寫入到文件中 fs.appendFileSync(filePath, fs.readFileSync(chunksPath + hash + '-' +i)); // 刪除本次使用的chunk fs.unlinkSync(chunksPath + hash + '-' +i); } fs.rmdirSync(chunksPath); // 文件合并成功,可以把文件信息進(jìn)行入庫。 ctx.status = 200; ctx.res.end('Success'); })
以上就是文件分片上傳的基本過程,過程中加入了上傳進(jìn)度條、暫停和開始上傳操作,見詳細(xì)代碼
到此,關(guān)于“JavaScript怎么實現(xiàn)大文件分片上傳處理”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。