vue項目實現文件下載進度條功能
平時業務中下載文件方式常見的有倆種:
第一種,直接訪問服務器的文件地址,自動下載文件;
第二種 ,服務器返回blob文件流,再對文件流進行處理和下載。
一般小文件適用於第一種下載方案,不占用過多服務器資源,而對於體積龐大的文件,常常使用文件流的方式進行傳輸,如圖:
文件流傳輸成功後通過代碼可以立即發起瀏覽器下載該文件流:
這種方式也有弊端,在文件流傳輸過程中,用戶無法感知文件流的傳輸狀態(進度),會造成一些困擾(無法確定當前下載操作是否已經生效)。針對這種情況,我們可以在頁面顯示文件流的狀態和傳輸進度,提高頁面交互性和友好性。
接下來是具體實現:
封裝js方法:
/** * @param {Object} data: {url: 文件地址, download: 文件名稱} */ export function downLoadAll(data) { let downProgress = {}; let uniSign = new Date().getTime() + ''; // 可能會連續點擊下載多個文件,這裡用時間戳來區分每一次下載的文件 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對象中的loaded表示已經下載的數量,total表示總數量,這裡計算出百分比 store.commit('caseInformation/SET_PROGRESS', {path: uniSign, 'progress': downProgress}) // 將此次下載的文件名和下載進度組成對象再用vuex狀態管理 }}).then( (res)=>{ // 文件流傳輸完成後,開啟文件下載 if(data.downLoad){ jsFileDownload(res.data,data.downLoad+'.'+data.url.replace(/.+\./,"")); // jsFileDownLoad是用來下載文件流的,下載插件: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('該文件無法下載') }) }
store中的caseInfomation.js:
... const state = { progressList: [], // 文件下載進度列表 ... } const mutations = { 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) // 刪除進度列表中的進度對象 }, ... }
頁面顯示進度彈框代碼,downLoadNotice.vue:
<template> </template> <script> import { mapState } from 'vuex' export default { name: 'downLoadNotice', computed: { ...mapState({ 'progressList': state => state.caseInformation.progressList }) }, data() { return { notify: {} // 用來維護下載文件進度彈框對象 } }, watch: { // 監聽進度列表 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)) { // 如果頁面已經有該進度對象的彈框,則更新它的進度progress domList.find(i => i.className == item.path).innerHTML = item.progress + '%' } else { if (item.progress === null) { // 此處容錯處理,如果後端傳輸文件流報錯,刪除當前進度對象 this.$store.commit('caseInformation/DEL_PROGRESS', item.path) return }// 如果頁面中沒有該進度對象所對應的彈框,頁面新建彈框,並在notify中加入該彈框對象,屬性名為該進度對象的path(上文可知path是唯一的),屬性值為$notify(element ui中的通知組件)彈框對象 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>`, // 顯示下載百分比,類名為進度對象的path(便於後面更新進度百分比) showClose: false, duration: 0 }) } console.log(item.progress + '%', '-------------------------->') if (item.progress == 100) { // 如果下載進度到瞭100%,關閉該彈框,並刪除notify中維護的彈框對象 this.notify[item.path].close() // delete this.notify[item.path] 上面的close()事件是異步的,這裡直接刪除會報錯,利用setTimeout,將該操作加入異步隊列 setTimeout(() => { delete this.notify[item.path] }, 1000) this.$store.commit('caseInformation/DEL_PROGRESS', item.path)// 刪除caseInformation中state的progressList中的進度對象 } }) }, deep: true } } } </script> <style scoped> </style>
以上代碼我們可以封裝到mixins文件夾中,在頁面內用mixins混入:
頁面中觸發下載操作:
downLoad(item){ let downData = { url: `ipdoc${item.url}`, downLoad: item.fileName } this.$commonUtils.downLoadAll(downData) // 下載 },
最終頁面的效果:
最後註意一點,以上的下載進度並不是真正下載的文件,而是文件流,文件流下載完成後,再通過上文的js-file-download插件下載真正的文件!
到此這篇關於vue項目實現文件下載進度條的文章就介紹到這瞭,更多相關vue文件下載進度條內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Vue文件下載進度條的實現過程
- vue實現el-menu和el-tab聯動的示例代碼
- websocket+Vuex實現一個實時聊天軟件
- 使用Vue3+Vant組件實現App搜索歷史記錄功能(示例代碼)
- Vuex數據的存儲與獲取方式