上传文件中出现内存比较大的情况要如何处理?其实不论是用户端还是服务端,假如采用一次性进行读取发送、接收都是不可取,很容易导致内存问题。这样我们可以考虑采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达到最大效率。
本篇文章主要为大家讲述就是基于springboot+vue实现的文件上传,本现在我就来说说vue实现文件上传的步骤及代码实现,服务端(springboot)的实现步骤及实现请移步本人的另一篇文章:
springboot大文件上传、分片上传、断点续传、秒传
上传分步:
本人分析上传总共分为:
MD5读取文件,获取文件的MD5编码
请求服务端判断文件是否上传,如上传完成就直接返回文件地址
如未上传,判断是否是断点续传
判断是并发上传还是顺序上传
开始分片文件上传,分片上传完成后写入已上传列表中
判断是否上传完成
直接上代码
文件上传:
import md5 from'js-md5'//引入MD5加密 import UpApi from' /api/common.js' import{concurrentExecution}from' /utils/jnxh' /** *文件分片上传 * params file{File}文件 * params pieceSize{Number}分片大小默认3MB * params concurrent{Number}并发数量默认2 * params process{Function}进度回调函数 * params success{Function}成功回调函数 * params error{Function}失败回调函数 */ export const uploadByPieces=({ file, pieceSize=3, concurrent=3, success, process, error })=>{ //如果文件传入为空直接return返回 if(!file||file.length<1){ return error('文件不能为空') } let fileMD5=''//总文件列表 const chunkSize=pieceSize*1024*1024//1MB一片 const chunkCount=Math.ceil(file.size/chunkSize)//总片数 const chunkList=[]//分片列表 let uploaded=[]//已经上传的 let fileType=''//文件类型 //获取md5 /*** *获取md5 **/ const readFileMD5=()=>{ //读取视频文件的md5 fileType=file.name.substring(file.name.lastIndexOf('.')+1,file.name.length) console.log('获取文件的MD5值') let fileRederInstance=new FileReader() console.log('file',file) fileRederInstance.readAsBinaryString(file) fileRederInstance.addEventListener('load',e=>{ let fileBolb=e.target.result fileMD5=md5(fileBolb) var index=file.name.lastIndexOf('.') var tp=file.name.substring(index+1,file.name.length) let form=new FormData() form.append('filename',file.name) form.append('identifier',fileMD5) form.append('objectType',fileType) form.append('chunkNumber',1) UpApi.uploadChunk(form).then(res=>{ if(res.skipUpload){ console.log('文件已被上传') success&&success(res) }else{ //判断是否是断点续传 if(res.uploaded&&res.uploaded.length!=0){ uploaded=[].concat(res.uploaded) } console.log('已上传的分片:'+uploaded) //判断是并发上传或顺序上传 if(concurrent==1||chunkCount==1){ console.log('顺序上传') sequentialUplode(0) }else{ console.log('并发上传') concurrentUpload() } } }).catch((e)=>{ console.log('文件合并错误') console.log(e) }) }) } /*** *获取每一个分片的详情 **/ const getChunkInfo=(file,currentChunk,chunkSize)=>{ let start=currentChunk*chunkSize let end=Math.min(file.size,start+chunkSize) let chunk=file.slice(start,end) return{ start, end, chunk } } /*** *针对每个文件进行chunk处理 **/ const readChunkMD5=()=>{ //针对单个文件进行chunk上传 for(var i=0;i<chunkCount;i++){ const{ chunk }=getChunkInfo(file,i,chunkSize) //判断已经上传的分片中是否包含当前分片 if(uploaded.indexOf(i+'')==-1){ uploadChunk({ chunk, currentChunk:i, chunkCount }) } } } /*** *原始上传 **/ const uploadChunk=(chunkInfo)=>{ var sd=parseInt((chunkInfo.currentChunk/chunkInfo.chunkCount)*100) console.log(sd,'进度') process(sd) console.log(chunkInfo,'分片大小') let inde=chunkInfo.currentChunk+1 if(uploaded.indexOf(inde+'')>-1){ const{ chunk }=getChunkInfo(file,chunkInfo.currentChunk+1,chunkSize) uploadChunk({ chunk, currentChunk:inde, chunkCount }) }else{ var index=file.name.lastIndexOf('.') var tp=file.name.substring(index+1,file.name.length) //构建上传文件的formData let fetchForm=new FormData() fetchForm.append('identifier',fileMD5) fetchForm.append('chunkNumber',chunkInfo.currentChunk+1) fetchForm.append('chunkSize',chunkSize) fetchForm.append('currentChunkSize',chunkInfo.chunk.size) const chunkfile=new File([chunkInfo.chunk],file.name) fetchForm.append('file',chunkfile) //fetchForm.append('file',chunkInfo.chunk) fetchForm.append('filename',file.name) fetchForm.append('relativePath',file.name) fetchForm.append('totalChunks',chunkInfo.chunkCount) fetchForm.append('totalSize',file.size) fetchForm.append('objectType',tp) //执行分片上传 let config={ headers:{ 'Content-Type':'application/json', 'Accept':'*/*' } } UpApi.uploadChunk(fetchForm,config).then(res=>{ if(res.code==200){ console.log('分片上传成功') uploaded.push(chunkInfo.currentChunk+1) //判断是否全部上传完 if(uploaded.length==chunkInfo.chunkCount){ console.log('全部完成') success(res) process(100) }else{ const{ chunk }=getChunkInfo(file,chunkInfo.currentChunk+1,chunkSize) uploadChunk({ chunk, currentChunk:chunkInfo.currentChunk+1, chunkCount }) } }else{ console.log(res.msg) } }).catch((e)=>{ error&&error(e) }) //if(chunkInfo.currentChunk<chunkInfo.chunkCount){ //setTimeout(()=>{ // //},1000) //} } } /*** *顺序上传 **/ const sequentialUplode=(currentChunk)=>{ const{ chunk }=getChunkInfo(file,currentChunk,chunkSize) let chunkInfo={ chunk, currentChunk, chunkCount } var sd=parseInt((chunkInfo.currentChunk/chunkInfo.chunkCount)*100) process(sd) console.log('当前上传分片:'+currentChunk) let inde=chunkInfo.currentChunk+1 if(uploaded.indexOf(inde+'')>-1){ console.log('分片【'+currentChunk+'】已上传') sequentialUplode(currentChunk+1) }else{ let uploadData=createUploadData(chunkInfo) let config={ headers:{ 'Content-Type':'application/json', 'Accept':'*/*' } } //执行分片上传 UpApi.uploadChunk(uploadData,config).then(res=>{ if(res.code==200){ console.log('分片【'+currentChunk+'】上传成功') uploaded.push(chunkInfo.currentChunk+1) //判断是否全部上传完 if(uploaded.length==chunkInfo.chunkCount){ console.log('全部完成') success(res) process(100) }else{ sequentialUplode(currentChunk+1) } }else{ console.log(res.msg) } }).catch((e)=>{ error&&error(e) }) } } /*** *并发上传 **/ const concurrentUpload=()=>{ for(var i=0;i<chunkCount;i++){ chunkList.push(Number(i)) } console.log('需要上传的分片列表:'+chunkList) concurrentExecution(chunkList,concurrent,(curItem)=>{ return new Promise((resolve,reject)=>{ const{ chunk }=getChunkInfo(file,curItem,chunkSize) let chunkInfo={ chunk, currentChunk:curItem, chunkCount } var sd=parseInt((chunkInfo.currentChunk/chunkInfo.chunkCount)*100) process(sd) console.log('当前上传分片:'+curItem) let inde=chunkInfo.currentChunk+1 if(uploaded.indexOf(inde+'')==-1){ //构建上传文件的formData let uploadData=createUploadData(chunkInfo) //请求头 let config={ headers:{ 'Content-Type':'application/json', 'Accept':'*/*' } } UpApi.uploadChunk(uploadData,config).then(res=>{ if(res.code==200){ uploaded.push(chunkInfo.currentChunk+1) console.log('已经上传完成的分片:'+uploaded) //判断是否全部上传完 if(uploaded.length==chunkInfo.chunkCount){ success(res) process(100) } resolve() }else{ reject(res) console.log(res.msg) } }).catch((e)=>{ reject(res) error&&error(e) }) }else{ console.log('分片【'+chunkInfo.currentChunk+'】已上传') resolve() } }) }).then(res=>{ console.log('finish',res) }) } /*** *创建文件上传参数 **/ const createUploadData=(chunkInfo)=>{ let fetchForm=new FormData() fetchForm.append('identifier',fileMD5) fetchForm.append('chunkNumber',chunkInfo.currentChunk+1) fetchForm.append('chunkSize',chunkSize) fetchForm.append('currentChunkSize',chunkInfo.chunk.size) const chunkfile=new File([chunkInfo.chunk],file.name) fetchForm.append('file',chunkfile) //fetchForm.append('file',chunkInfo.chunk) fetchForm.append('filename',file.name) fetchForm.append('relativePath',file.name) fetchForm.append('totalChunks',chunkInfo.chunkCount) fetchForm.append('totalSize',file.size) fetchForm.append('objectType',fileType) return fetchForm } readFileMD5()//开始执行代码 }
并发控制:
/** *并发执行 * params list{Array}-要迭代的数组 * params limit{Number}-并发数量控制数,最好小于3 * params asyncHandle{Function}-对`list`的每一个项的处理函数,参数为当前处理项,必须return一个Promise来确定是否继续进行迭代 * return{Promise}-返回一个Promise值来确认所有数据是否迭代完成 */ export function concurrentExecution(list,limit,asyncHandle){ //递归执行 let recursion=(arr)=>{ //执行方法arr.shift()取出并移除第一个数据 return asyncHandle(arr.shift()).then(()=>{ //数组还未迭代完,递归继续进行迭代 if(arr.length!==0){ return recursion(arr) }else{ return'finish' } }) } //创建新的并发数组 let listCopy=[].concat(list) //正在进行的所有并发异步操作 let asyncList=[] limit=limit>listCopy.length?listCopy.length:limit console.log(limit) while(limit--){ asyncList.push(recursion(listCopy)) } //所有并发异步操作都完成后,本次并发控制迭代完成 return Promise.all(asyncList) }
大家有学会关于vue大文件分片上传(断点续传、并发上传、秒传)嘛!希望大家可以好好看看。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/130355.html
前段时间在w3ctech技术交流会中分享了基于 HTML5 技术的文件上传组件,由于ppt携带的信息非常有限,故在此整理成文章分享出来,供感兴趣的同学阅读。 HTML VS FLASH 对于文件上传,相信还有不少同学还停留在FLASH时代,其实...
...什么使用Vue-Simple-Uploader 最近用到了Vue + Spring Boot来完成文件上传的操作,踩了一些坑,对比了一些Vue的组件,发现了一个很好用的组件——Vue-Simple-Uploader 再说说为什么选用这个组件,对比vue-ant-design和element-ui的上传组件,它能...
话前 上传大文件上传的教程网上很多, 但是大部分没给出一个比较完整的出来, 这个博客给出的是前后端一套完整的解决方案, 其中前端没有使用第三方上传库, 希望能帮到有同样需求的朋友们. 大文件分片上传的好处在这里就...
话前 上传大文件上传的教程网上很多, 但是大部分没给出一个比较完整的出来, 这个博客给出的是前后端一套完整的解决方案, 其中前端没有使用第三方上传库, 希望能帮到有同样需求的朋友们. 大文件分片上传的好处在这里就...
...一在一个线程中进行。获取uploadId这块需要先对本地缓存文件进行获取,如未拿到,就会直接重新生成新的uploadId直接去进行分片上传,否则会对记录的id进行之前上传了多少片进行还原,继续原来的位置继续上传。分片上传部分...
阅读 117·2023-03-27 18:33
阅读 251·2023-03-27 17:49
阅读 180·2023-03-26 17:27
阅读 110·2023-03-26 17:14
阅读 122·2023-03-17 21:13
阅读 112·2023-03-17 08:28
阅读 881·2023-02-27 22:32
阅读 366·2023-02-27 22:27