Vue.js实现大文件分片md5断点续传
背景
根据部门的业务需求,需要在网络状态不良的情况下上传很大的文件(1G+)。
其中会遇到的问题:
1,文件过大,超出服务端的请求大小限制;
2,请求时间过长,请求超时;
3,传输中断,必须重新上传导致前功尽弃。
解决方案实现思路,拿到文件,保存文件唯一性标识,切割文件、分片上传、文件MD5验证、断点续传、手动重试上传。
前言
鉴于过往有使用过webupload文件上传组件的经验,于是此次采用的是Plupload作为替换。Plupload是一款由著名的web编辑器TinyMCE团队开发的上传组件,简单易用且功能强大。
Plupload有以下功能和特点
- 拥有多种上传方式:HTML5、flash、silverlight以及传统的
<input type=”file” />
。Plupload会自动侦测当前的环境,选择最合适的上传方式,并且会优先使用HTML5的方式。所以你完全不用去操心当前的浏览器支持哪些上传方式,Plupload会自动为你选择最合适的方式。 - 支持以拖拽的方式来选取要上传的文件
- 支持在前端压缩图片,即在图片文件还未上传之前就对它进行压缩
- 可以直接读取原生的文件数据,这样的好处就是例如可以在图片文件还未上传之前就能把它显示在页面上预览
- 支持把大文件切割成小片进行上传,因为有些浏览器对很大的文件比如几G的一些文件无法上传。
环境
- vue2.x
- webpack3.x
- axios
代码
npm安装plupload,文件引入组件,
1 <uploader browse_button="upload_area" 2 :max_retries="3" 3 :url="action" 4 :headers="headers" 5 chunk_size="10MB" 6 drop_element="upload_area" 7 @disableBrowse="!loading" 8 :BeforeUpload="beforeUpload" 9 :ChunkUploaded="chunkUploaded" 10 :FilesAdded="filesAdded" 11 :StateChanged="stateChanged" 12 @inputUploader="inputUploader" />
初始化方法filesAdded(),每次上传前清空队列的其他文件,保证上传的一致性。其次对文件类型进行判断过滤fileType(),文件进入时进行总md5一次fileMd5(),然后进入文件分片chunkCheckStatus(),每个分片都要进行md5并与后台进行校验fileMd5(),确保文件在中断后继续上传的准确性。
1 filesAdded (up, files) { 2 // 删除上传队列中其他文件,只保留最近上传的文件 3 let fileLen = files.length, that = this 4 if (fileLen > 1) { 5 files = files.splice(0, fileLen - 1)// 清空上传队列 6 } 7 files.forEach((f) => { 8 f.status = -1 9 that.dataForm.file = f 10 that.fileType(f.getNative()) 11 if (that.loading) { 12 that.computeStatus = true 13 that.progress = 0 14 // 文件分片 15 let chunkSize = 2097152, // Read in chunks of 2MB 16 chunks = Math.ceil(f.size / chunkSize) 17 that.fileMd5(f.getNative(), (e, md5) => { 18 that.dataForm.md5 = md5 19 if (that.loading == true) { 20 that.count = 0 21 that.chunkCheckStatus(md5, that.dataForm.fileName, (uploader, dataList) => { 22 that.uploading = uploader 23 if (that.uploading == true) { 24 for (let chunk = 1; chunk <= chunks; chunk++) { 25 that.fileChunkFile(f.getNative(), chunk, (e, chunkFile) => { 26 that.fileMd5(chunkFile, (e, blockMd5) => { 27 that.PostFile(up, chunkFile, chunk, chunks, md5, blockMd5) 28 }) 29 }) 30 } 31 } else { 32 // 去重 33 that.progress = 0 34 for (let chunk = 1; chunk <= chunks; chunk++) { 35 let status = 0 36 dataList.some((item) => { 37 if (item.chunk == chunk) { 38 status = 1 39 return false 40 } 41 }) 42 if (status == 0) { 43 that.fileChunkFile(f.getNative(), chunk, (e, chunkFile) => { 44 that.fileMd5(chunkFile, (e, blockMd5) => { 45 that.PostFile(up, chunkFile, chunk, chunks, md5, blockMd5) 46 }) 47 }) 48 } 49 } 50 } 51 }) 52 } 53 }) 54 } 55 }) 56 }
文件md5方法,这里使用了SparkMD5,import SparkMD5 from 'spark-md5'
1 fileMd5 (file, callback) { 2 let that = this 3 var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, 4 file = file, 5 chunkSize = 2097152, // Read in chunks of 2MB 6 chunks = Math.ceil(file.size / chunkSize), 7 currentChunk = 0, 8 spark = new SparkMD5.ArrayBuffer(), 9 fileReader = new FileReader() 10 fileReader.onload = function (e) { 11 console.log('read chunk nr', currentChunk + 1, 'of', chunks) 12 spark.append(e.target.result) // Append array buffer 13 currentChunk++ 14 if (currentChunk < chunks) { 15 loadNext() 16 } else { 17 let blockMd5 = '' 18 blockMd5 = spark.end() 19 callback(null, blockMd5) 20 } 21 } 22 fileReader.onerror = function () { 23 callback('oops, something went wrong.') 24 } 25 function loadNext () { 26 var start = currentChunk * chunkSize, 27 end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize 28 fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)) 29 } 30 loadNext() 31 }
文件分片上传方法,验证总分片信息后,把每个分片进行md5加密并上传校验,这里有写进度条相关的控制,不一一展示
1 chunkCheckStatus (md5, fileName, callback) { 2 this.$http({ 3 url: this.$http.adornUrl('/biz/upload/getFileBlockStatus'), 4 method: 'get', 5 params: this.$http.adornParams({ 6 md5: md5, 7 fileName: fileName 8 }) 9 }).then(({ data }) => { 10 if (data && data.code === 0) { 11 if (data.list != null) { 12 this.uploading = false 13 this.chunkCheckData = [] 14 data.list.map((item, index) => { 15 if (item.isUpload == true) { 16 this.count++ 17 this.chunkCheckData.push(item) 18 } 19 }) 20 callback(this.uploading, this.chunkCheckData) 21 return 22 } 23 this.uploading = true 24 callback(this.uploading) 25 } else { 26 this.$message.error(data.msg) 27 this.loading = false 28 this.computeStatus = false 29 return false 30 } 31 }) 32 }
总结
上图可以清晰的说明文件加密上传的传输流程,在文件上传加密及Content-Type格式需要与后台协商一致,通常有base64、multipart/form-data两种类型,要清晰了解分片算法及md5,自定义上传文件异步代码,除此之外,可拖拽上传文件、进度条控制等可更好的丰富文件上传体验。
参考文章:http://blog.ncmem.com/wordpress/2023/12/19/vue-js%e5%ae%9e%e7%8e%b0%e5%a4%a7%e6%96%87%e4%bb%b6%e5%88%86%e7%89%87md5%e6%96%ad%e7%82%b9%e7%bb%ad%e4%bc%a0/
欢迎入群一起讨论
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
2019-12-19 局域网大附件上传,支持断点续传
2019-12-19 网页大附件上传,支持断点续传
2019-12-19 B/S大附件上传,支持断点续传
2019-12-19 jsp大附件上传,支持断点续传
2019-12-19 php大附件上传,支持断点续传
2019-12-19 asp.net大附件上传,支持断点续传