需求场景
大文件下载,花费的时间比较长,没有任何提示,用户体验很差。
需要优化,提示文件在下载中,并且显示进度百分比。
实现步骤
1.下载文件的方法,需要拿到当前进度。
2.每一次下载进度更新,需要监听变化,并且刷新页面显示的数据。
3.封装一个文件下载进度的组件。
下面一步步来实现:
1.获取文件下载进度
axios作为一个易用、简洁且高效的http库,有没有获取下载进度的方法呢?
打开axios官网查看一下,文档中有一个对原生进度事件的处理的方法
onDownloadProgress
允许为下载处理进度事件。
在项目中,我已经对axios进行了封装,并且增加了请求拦截器和响应拦截器。
在封装接口的文件interface.js中,新增一个下载文件,并且获取下载进度的方法。
exportconstdownFileProgress=(url,parameter,callback,totalSize,uniSign)=>{returnaxios({url:url,params:parameter,method:'get',responseType:'blob',onDownloadProgress(progress){callback(progress,totalSize,uniSign)}})}
2.通过vuex状态管理下载进度
下载进度对象,需要通过vuex状态管理。
在store文件下面的modules文件夹中,新建 downLoadProgress.js文件。
用来存放文件下载进度的数组progressList 和修改进度列表方法,都在这里面。
conststate={//文件下载进度progressList:[],progressError:'',}constmutations={SET_PROGRESS:(state,progressObj)=>{//修改进度列表if(state.progressList.length){//如果进度列表存在if(state.progressList.find(item=>item.path==progressObj.path)){//前面说的path时间戳是唯一存在的,所以如果在进度列表中找到当前的进度对象state.progressList.find(item=>item.path==progressObj.path).progress=progressObj.progress//改变当前进度对象的progress}}else{//当前进度列表为空,没有下载任务,直接将该进度对象添加到进度数组内state.progressList.push(progressObj)}},DEL_PROGRESS:(state,props)=>{state.progressList.splice(state.progressList.findIndex(item=>item.path==props),1)//删除进度列表中的进度对象},CHANGE_SETTING:(state,{key,value})=>{//eslint-disable-next-lineno-prototype-builtinsif(state.hasOwnProperty(key)){state[key]=value}}}constactions={changeSetting({commit},data){commit('CHANGE_SETTING',data)}}exportdefault{namespaced:true,state,mutations,actions}
3.新增一个显示进度弹框的组件
<template></template><script>import{mapState}from'vuex'exportdefault{name:'downLoadNotice',computed:{...mapState({'progressList':state=>state.downLoadProgress.progressList})},data(){return{notify:{}//用来维护下载文件进度弹框对象}},watch:{//监听进度列表progressList:{handler(n){letdata=JSON.parse(JSON.stringify(n))data.forEach(item=>{constdomList=[...document.getElementsByClassName(item.path)]if(domList.find(i=>i.className==item.path)){//如果页面已经有该进度对象的弹框,则更新它的进度progressif(item.progress)domList.find(i=>i.className==item.path).innerHTML=item.progress+'%'if(item.progress===null){//此处容错处理,如果后端传输文件流报错,删除当前进度对象this.$store.commit('downLoadProgress/DEL_PROGRESS',item.path)this.$notify.error({title:'错误',message:'文件下载失败!'});}}else{//如果页面中没有该进度对象所对应的弹框,页面新建弹框,并在notify中加入该弹框对象,属性名为该进度对象的path(上文可知path是唯一的),属性值为$notify(elementui中的通知组件)弹框对象this.notify[item.path]=this.$notify.success({//title:'info',dangerouslyUseHTMLString:true,customClass:'progress-notify',message:`<pstyle="width:100px;">正在下载<spanclass="${item.path}"style="float:right">${item.progress}%</span></p>`,//显示下载百分比,类名为进度对象的path(便于后面更新进度百分比)showClose:false,duration:0})}console.log(item.progress+'%','-------------------------->')if(item.progress==100){//如果下载进度到了100%,关闭该弹框,并删除notify中维护的弹框对象this.notify[item.path].close()//deletethis.notify[item.path]上面的close()事件是异步的,这里直接删除会报错,利用setTimeout,将该操作加入异步队列setTimeout(()=>{deletethis.notify[item.path]},1000)this.$store.commit('downLoadProgress/DEL_PROGRESS',item.path)//删除caseInformation中state的progressList中的进度对象}})},deep:true}}}</script><stylelang="scss"scoped></style>
axios请求的接口方法,vuex状态管理,显示进度弹框的组件,都已经完成。
接下来,在需要显示下载进度的页面,引入进度弹框的组件。
修改一些之前下载文件的方法。
使用之前封装的 downFileProgress 接口下载文件,在回调函数callBackProgress中出来下载文件的进度。
dowOrgFile(id,fileFprimalnam){constparams={id:id,templateId:this.templateId,};lettotalSize=0;letobj=this.fileList.find(element=>element.id==id);if(obj)totalSize=obj.fileSize;letuniSign=newDate().getTime()+'';//可能会连续点击下载多个文件,这里用时间戳来区分每一次下载的文件this.$api.downFileProgress(this.url.dowOrgFile,params,this.callBackProgress,totalSize,uniSign).then((data)=>{if(!data){this.$sweetAlert.errorWithTimer("文件下载失败!");return;}if(typeofwindow.navigator.msSaveBlob!=="undefined"){window.navigator.msSaveBlob(newBlob([data]),fileFprimalnam);}else{consturl=window.URL.createObjectURL(newBlob([data]));constlink=document.createElement("a");link.style.display="none";link.href=url;link.setAttribute("download",fileFprimalnam);document.body.appendChild(link);link.click();document.body.removeChild(link);window.URL.revokeObjectURL(url);}});},callBackProgress(progress,totalSize,uniSign){lettotal=progress.total||totalSize;letloaded=progress.loaded;this.progressSign=uniSign;//progress对象中的loaded表示已经下载的数量,total表示总数量,这里计算出百分比letdownProgress=Math.round(100*loaded/total)//将此次下载的文件名和下载进度组成对象再用vuex状态管理this.$store.commit('downLoadProgress/SET_PROGRESS',{path:uniSign,'progress':downProgress})},
至此,下载文件显示进度条的功能,已实现。
参考文章
onDownloadProgress和onUploadProgress配置进度条
https://www.cnblogs.com/blackbentel/p/10981516.html
vue项目实现文件下载进度条功能
https://www.jb51.net/article/223655.htm