大文件上传
用于大文件的分片上传,秒传,断点续传,多线程上传。
md5的抽片生成或者md5全文件内容生成
-
使用原生input进行上传–在vue2中使用的例子
accept限制选中文件的格式
<label class="file-label" style="cursor:pointer;"> 上传资料 <input style="display:none" accept="image/bmp, image/jpeg, image/png, audio/mpeg, video/mp4, application/x-zip- compressed, text/plain, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf" ref="input_file" class="input_file" id="File" type="file" @change='upload' /> </label>
-
上传校验文件类型或大小
//上传文件 async upload(e) { let fileAccept = "image/bmp, image/jpeg, image/png, audio/mpeg, video/mp4, application/x-zip-compressed, text/plain, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf" if (fileAccept.indexOf(e.target.files[0].type) === -1) { Message.error('非格式类型文件,请重新上传!') return; } const file = e.target.files[0]; if (!file) return; if (event[0].size > 5 * 1024 * 1024 * 1024) { Message.warning("单个文件上传大小已超出最大限制(5GB)") return } let fileMd5 = await this.calcFileMD5(file) let result = await this.$mApi.getAttachmentState(fileMd5) //文件已上传 if (result.data.state == 'EXIST') { this.uploadDocument(result, file.name, fileMd5) } else { this.uploadSingle(file, fileMd5, result, '正在上传资料', 3); } },
-
文件生成md5-若为zip则使用抽片计算,小于20片就全部计算其余文件全部计算
//导入计算文件MD5 calcFileMD5(file) { //判断哪一种要抽片生成md5 if (file.type == 'application/zip' || file.type == 'application/x-zip-compressed') { return new Promise((resolve, reject) => { let chunkSize = 1024 * 1024 * 20, // 20M chunks = Math.ceil(file.size / chunkSize), currentChunk = 0, currentChunkNum = 0, spark = new SparkMD5.ArrayBuffer(), fileReader = new FileReader(); this.loading = this.$loading({ text: '正在校验文件:' + (currentChunk / chunks * 100).toFixed(2) + '%' }); let blocks = 20 let jumpNum = Math.ceil(chunks/blocks) console.log(chunks,'chunks',jumpNum) // 计算抽样的起始位置和数量 let sampleStart = chunkSize * 2 , // 开始 sampleEnd = chunkSize * (chunks - 2), //结束 sampleChunks = Math.ceil((sampleEnd - sampleStart) / chunkSize); // 取样块数 fileReader.onload = (e) => { spark.append(e.target.result); currentChunkNum += jumpNum; currentChunk ++; // 如果是抽样的片段,则计算抽样的 MD5 if (currentChunk >= blocks && currentChunk < chunks - 2 && (currentChunk - 2) % Math.ceil(chunks / sampleChunks) === 0) { let sampleSpark = new SparkMD5.ArrayBuffer(); sampleSpark.append(e.target.result); resolve(sampleSpark.end()); this.loading.close(); return; } console.log(currentChunk,'currentChunk',chunks) if (currentChunk < chunks) { console.log(1) this.loading.setText('正在校验文件:' + (currentChunk / (blocks>chunks?chunks:blocks) * 100).toFixed(2) + '%') loadNext(); } else { resolve(spark.end()); this.loading.close() } }; fileReader.onerror = (e) => { reject(fileReader.error); reader.abort(); }; function loadNext() { let start, end; //抽片的逻辑 start = currentChunkNum * chunkSize; end = start + chunkSize >= file.size ? file.size : start + chunkSize; fileReader.readAsArrayBuffer(file.slice(start, end)); } loadNext(); }); } else { // 不是zip类型的压缩包 return new Promise((resolve, reject) => { let chunkSize = 1024 * 1024 * 20, // 20M chunks = Math.ceil(file.size / chunkSize), currentChunk = 0, spark = new SparkMD5.ArrayBuffer(), fileReader = new FileReader(); this.loading = this.$loading({ text: '正在校验文件:' + (currentChunk / chunks * 100).toFixed(2) + '%' }); fileReader.onload = (e) => { spark.append(e.target.result); currentChunk++; if (currentChunk < chunks) { console.log(1) this.loading.setText('正在校验文件:' + (currentChunk / chunks * 100).toFixed(2) + '%') loadNext(); } else { resolve(spark.end()); this.loading.close() } }; fileReader.onerror = (e) => { reject(fileReader.error); reader.abort(); }; function loadNext() { let start = currentChunk * chunkSize, end = start + chunkSize >= file.size ? file.size : start + chunkSize; fileReader.readAsArrayBuffer(file.slice(start, end)); } loadNext(); }); } },
-
多线程上传
//多线程上传 async asyncPool(poolLimit, array, iteratorFn) { const ret = []; // 存储所有的异步任务 const executing = []; // 存储正在执行的异步任务 for (let index = 0; index < array.length; index++) { // 调用iteratorFn函数创建异步任务 const p = Promise.resolve().then(() => iteratorFn(array[index], index)); ret.push(p); // 保存新的异步任务 // 当poolLimit值小于或等于总任务个数时,进行并发控制 if (poolLimit <= array.length) { // 当任务完成后,从正在执行的任务数组中移除已完成的任务 const e = p.then(() => executing.splice(executing.indexOf(e), 1)); executing.push(e); // 保存正在执行的异步任务 if (executing.length >= poolLimit) { await Promise.race(executing); // 等待较快的任务执行完成 } } } return Promise.all(ret); },
-
上传文件信息
// 文件上传信息 async uploadSingle(file, fileMd5, result, prompt, promptType) { if (!file) return; console.log(file, 'file') //所有文件块 let ckunkFile = await this.splitFile(file) let totalSize = file.size; //文件大小 let blockSize = 1024 * 1024 * 20; //块大小 let chunk = 0; // 当前块数 let chunks = Math.ceil(totalSize / blockSize) ? Math.ceil(totalSize / blockSize) : 1; //总块数 console.log(chunks, '总块数', promptType) let type = 'course' //obj看后端接口需要进行修改 let obj = { filename: file.name, taskId: fileMd5, type: type,//文件类型 chunks: chunks, force: false } this.uploadFile(ckunkFile, obj, result.data.missChunkList, 3, prompt, promptType); this.$refs.input_file.value = null; },
-
上传文件
// 上传文件 uploadFile(ckunkFile, obj, missChunkList, poolLimit = 3) { return this.asyncPool(poolLimit, ckunkFile, (item, index) => { if (Array.isArray(missChunkList) && !missChunkList?.includes(index)) { let res = Promise.resolve() return res } else { let formData = new FormData(); formData.set("filename", obj.filename); // 文件名 formData.set("taskId", obj.taskId); // 上传任务ID(UUID)同一文件的不同分块的taskId相同 formData.set("type", obj.type); // 上传任务ID(UUID)同一文件的不同分块的taskId相同 formData.set("chunks", obj.chunks); // 总块数 formData.set("force", false); // 存放子目录名称 formData.set('file', item) formData.set('chunk', index) this.loading = this.$loading({ text: prompt + '...' }); let res = this.$mApi.attachmentChunk(formData, index).then(async (result) => { let { data, code, msg, usrMsg } = result if (code == 4) { Message.error(usrMsg) this.loading.close(); return; } if (data.progress > 0 && data.progress < 1) { this.loading.setText(prompt + ':' + (data.progress * 100).toFixed(2) + '%') } if (data.progress == 1) { //处理上传完成的逻辑 } }) return res } }); },