vue項(xiàng)目實(shí)現(xiàn)文件下載進(jìn)度條功能
平時(shí)業(yè)務(wù)中下載文件方式常見(jiàn)的有倆種:
第一種,直接訪問(wèn)服務(wù)器的文件地址,自動(dòng)下載文件;
第二種 ,服務(wù)器返回blob文件流,再對(duì)文件流進(jìn)行處理和下載。
一般小文件適用于第一種下載方案,不占用過(guò)多服務(wù)器資源,而對(duì)于體積龐大的文件,常常使用文件流的方式進(jìn)行傳輸,如圖:
文件流傳輸成功后通過(guò)代碼可以立即發(fā)起瀏覽器下載該文件流:
這種方式也有弊端,在文件流傳輸過(guò)程中,用戶無(wú)法感知文件流的傳輸狀態(tài)(進(jìn)度),會(huì)造成一些困擾(無(wú)法確定當(dāng)前下載操作是否已經(jīng)生效)。針對(duì)這種情況,我們可以在頁(yè)面顯示文件流的狀態(tài)和傳輸進(jìn)度,提高頁(yè)面交互性和友好性。
接下來(lái)是具體實(shí)現(xiàn):
封裝js方法:
/** * @param {Object} data: {url: 文件地址, download: 文件名稱} */ export function downLoadAll(data) { let downProgress = {}; let uniSign = new Date().getTime() + ''; // 可能會(huì)連續(xù)點(diǎn)擊下載多個(gè)文件,這里用時(shí)間戳來(lái)區(qū)分每一次下載的文件 axios.get( data.url, { responseType: 'blob', headers: { "Content-Type": "application/json; charset=utf-8" }, onDownloadProgress (progress) { downProgress = Math.round(100 * progress.loaded / progress.total) // progress對(duì)象中的loaded表示已經(jīng)下載的數(shù)量,total表示總數(shù)量,這里計(jì)算出百分比 store.commit('caseInformation/SET_PROGRESS', {path: uniSign, 'progress': downProgress}) // 將此次下載的文件名和下載進(jìn)度組成對(duì)象再用vuex狀態(tài)管理 }}).then( (res)=>{ // 文件流傳輸完成后,開(kāi)啟文件下載 if(data.downLoad){ jsFileDownload(res.data,data.downLoad+'.'+data.url.replace(/.+\./,"")); // jsFileDownLoad是用來(lái)下載文件流的,下載插件:npm i js-file-download,import引入:import jsFileDownLoad from 'js-file-download' } else { jsFileDownload(res.data, data.url.split('/')[data.url.split('/').length-1]); } }).catch((e)=>{ this.$message.error('該文件無(wú)法下載') }) }
store中的caseInfomation.js:
... const state = { progressList: [], // 文件下載進(jìn)度列表 ... } const mutations = { SET_PROGRESS: (state, progressObj)=>{ // 修改進(jìn)度列表 if(state.progressList.length){ // 如果進(jìn)度列表存在 if(state.progressList.find(item=>item.path == progressObj.path)){ // 前面說(shuō)的path時(shí)間戳是唯一存在的,所以如果在進(jìn)度列表中找到當(dāng)前的進(jìn)度對(duì)象 state.progressList.find(item=>item.path == progressObj.path).progress = progressObj.progress // 改變當(dāng)前進(jìn)度對(duì)象的progress } }else{ state.progressList.push(progressObj) // 當(dāng)前進(jìn)度列表為空,沒(méi)有下載任務(wù),直接將該進(jìn)度對(duì)象添加到進(jìn)度數(shù)組內(nèi) } }, DEL_PROGRESS: (state, props) => { state.progressList.splice(state.progressList.findIndex(item=>item.path == props), 1) // 刪除進(jìn)度列表中的進(jìn)度對(duì)象 }, ... }
頁(yè)面顯示進(jìn)度彈框代碼,downLoadNotice.vue:
<template> </template> <script> import { mapState } from 'vuex' export default { name: 'downLoadNotice', computed: { ...mapState({ 'progressList': state => state.caseInformation.progressList }) }, data() { return { notify: {} // 用來(lái)維護(hù)下載文件進(jìn)度彈框?qū)ο? } }, watch: { // 監(jiān)聽(tīng)進(jìn)度列表 progressList: { handler(n) { let data = JSON.parse(JSON.stringify(n)) data.forEach(item => { const domList = [...document.getElementsByClassName(item.path)] if (domList.find(i => i.className == item.path)) { // 如果頁(yè)面已經(jīng)有該進(jìn)度對(duì)象的彈框,則更新它的進(jìn)度progress domList.find(i => i.className == item.path).innerHTML = item.progress + '%' } else { if (item.progress === null) { // 此處容錯(cuò)處理,如果后端傳輸文件流報(bào)錯(cuò),刪除當(dāng)前進(jìn)度對(duì)象 this.$store.commit('caseInformation/DEL_PROGRESS', item.path) return }// 如果頁(yè)面中沒(méi)有該進(jìn)度對(duì)象所對(duì)應(yīng)的彈框,頁(yè)面新建彈框,并在notify中加入該彈框?qū)ο?,屬性名為該進(jìn)度對(duì)象的path(上文可知path是唯一的),屬性值為$notify(element ui中的通知組件)彈框?qū)ο? this.notify[item.path] = this.$notify.success({ // title: 'Info', dangerouslyUseHTMLString: true, message: `<p style="width: 100px;">正在下載<span class="${item.path}" style="float: right">${item.progress}%</span></p>`, // 顯示下載百分比,類名為進(jìn)度對(duì)象的path(便于后面更新進(jìn)度百分比) showClose: false, duration: 0 }) } console.log(item.progress + '%', '-------------------------->') if (item.progress == 100) { // 如果下載進(jìn)度到了100%,關(guān)閉該彈框,并刪除notify中維護(hù)的彈框?qū)ο? this.notify[item.path].close() // delete this.notify[item.path] 上面的close()事件是異步的,這里直接刪除會(huì)報(bào)錯(cuò),利用setTimeout,將該操作加入異步隊(duì)列 setTimeout(() => { delete this.notify[item.path] }, 1000) this.$store.commit('caseInformation/DEL_PROGRESS', item.path)// 刪除caseInformation中state的progressList中的進(jìn)度對(duì)象 } }) }, deep: true } } } </script> <style scoped> </style>
以上代碼我們可以封裝到mixins文件夾中,在頁(yè)面內(nèi)用mixins混入:
頁(yè)面中觸發(fā)下載操作:
downLoad(item){ let downData = { url: `ipdoc${item.url}`, downLoad: item.fileName } this.$commonUtils.downLoadAll(downData) // 下載 },
最終頁(yè)面的效果:
最后注意一點(diǎn),以上的下載進(jìn)度并不是真正下載的文件,而是文件流,文件流下載完成后,再通過(guò)上文的js-file-download插件下載真正的文件!
到此這篇關(guān)于vue項(xiàng)目實(shí)現(xiàn)文件下載進(jìn)度條的文章就介紹到這了,更多相關(guān)vue文件下載進(jìn)度條內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+springmvc導(dǎo)出excel數(shù)據(jù)的實(shí)現(xiàn)代碼
這篇文章主要介紹了vue+springmvc導(dǎo)出excel數(shù)據(jù)的實(shí)現(xiàn)代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06vue 如何從單頁(yè)應(yīng)用改造成多頁(yè)應(yīng)用
這篇文章主要介紹了vue 如何從單頁(yè)應(yīng)用改造成多頁(yè)應(yīng)用,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下2020-10-10vue簡(jiǎn)單實(shí)現(xiàn)轉(zhuǎn)盤抽獎(jiǎng)
這篇文章主要為大家詳細(xì)介紹了vue簡(jiǎn)單實(shí)現(xiàn)轉(zhuǎn)盤抽獎(jiǎng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03vue3中require報(bào)錯(cuò)require is not defined問(wèn)題及解決
這篇文章主要介紹了vue3中require報(bào)錯(cuò)require is not defined問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06vue+elementUI 復(fù)雜表單的驗(yàn)證、數(shù)據(jù)提交方案問(wèn)題
這篇文章主要介紹了vue+elementUI 復(fù)雜表單的驗(yàn)證、數(shù)據(jù)提交方案,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06vue3使用vue3-print-nb實(shí)現(xiàn)區(qū)域打印功能
這篇文章主要給大家介紹了關(guān)于vue3使用vue3-print-nb實(shí)現(xiàn)區(qū)域打印功能的相關(guān)資料,在日常操作中,相信很多人在Vue怎么用插件實(shí)現(xiàn)打印功能問(wèn)題上存在疑惑,需要的朋友可以參考下2023-07-07