产品需求:
文件导出时有时接口响应过长,想要个提示显示哪个文件正在下载中,不耽误其他的操作进行,下载完成自动清除提示。
实现效果:
文件下载时显示全局提示显示文件携带下载时间正在下载中,下载完成清除提示,如果在下载过程中出现窗口重新刷新,刷新结束后,自动根据未下载完成的文件重新发起下载请求。
实现过程:
1.封装api请求文件
const $axios = (opts, data, uniSign) => {
let httpDefaultOpts = { // http默认配置
method: opts.method,
url: opts.url,
responseType: opts.responseType || 'json', // 一般一个网站的responseType 都是一样的
withCredentials: true, // 是否允许带cookie这些
params: data,
data: data,
headers: {
'Content-Type': opts.contentType || 'application/json',
'Authorization': `Token ${getToken()}`
},
onDownloadProgress: opts.onDownloadProgress || null,
cancelToken: new axios.CancelToken(function (c) {
cancel = c // 记录当前请求的取消方法
})
}
if (opts.method === 'get') {
delete httpDefaultOpts.data
} else if (opts.method === 'delete') {
delete httpDefaultOpts.data
} else {
delete httpDefaultOpts.params
}
let promise = new Promise(function (resolve, reject) {
axios(httpDefaultOpts).then(
(res) => {
resolve(res.data)
}
).catch(
(error) => {
if(opts.responseType === 'blob') {
// 用于特殊处理blob文件流形式错误
if(store.getters.downProgress?.length > 0){
store.getters.downProgress.forEach(gress => {
if(gress.path === uniSign && gress.func) {
gress.func()
}else{
// 清空message信息
window.$MessageDestory()
}
});
}
}else {
}
reject(error)
}
)
})
return promise
}
2.封装了一个公共下载的js文件
/**
* @description 用于文件下载的接口
*/
export function downFileProgres(url, data, txt_name) {
let uniSign = new Date().getTime() + ''
const cur_time = dayjs().format('YYYY-MM-DD HH:mm:ss')
const pathclose = window.$MessageConfig(`${txt_name + '-' + cur_time}文件正在下载中...`, 0)
store.dispatch('setProgress', {'url': url, 'params': data, path: uniSign, 'txt_name': txt_name + '-' + cur_time, func: pathclose})
const service = {
method: 'get',
url: `/api${url}`,
responseType: 'blob',
onDownloadProgress: function (progress) {
if(Math.round(100 * progress.loaded / progress.total) === 100){
pathclose !== undefined && pathclose()
if(store.getters.downProgress?.length < 1) window.$MessageDestory()
}
}
}
return $axios(service, data, uniSign)
}
/**
* @description 强制刷新浏览器重新发起下载请求
*/
export function downFileAgin(data) {
const pathclose = window.$MessageConfig(`${data.txt_name}文件正在下载中...`, 0)
store.dispatch('setProgress', {'url': data.url, 'params': data.params, path: data.uniSign, 'txt_name': data.txt_name, func: data.pathclose})
const service = {
method: 'get',
url: `/api${data.url}`,
responseType: 'blob',
onDownloadProgress: function (progress) {
if(Math.round(100 * progress.loaded / progress.total) === 100){
pathclose !== undefined && pathclose()
store.dispatch('delProgress', data.path)
if(store.getters.downProgress?.length < 1) window.$MessageDestory()
}
}
}
return $axios(service, data.params, data.path)
}
3.封装vuex存贮文件的下载状态
const app = {
state: () => ({
downfile: []
}),
mutations: {
SET_PROGRESS: (state, fileObj)=>{
// 修改进度列表
state.downfile.push(fileObj)
},
DEL_PROGRESS: (state, props) => {
state.downfile.splice(state.downfile.findIndex(item=>item.path == props), 1) // 删除进度列表中的进度对象
},
CHANGE_SETTING: (state, data) => {
if(state.downfile.find(item=>item.path === data.path)){
const cur_index = state.downfile.findIndex(item=>item.path === data.path)
const new_obj = Object.assign({},{...data})
state.downfile.splice(cur_index,1,new_obj)
}
}
},
actions: {
changeSetting({ commit }, data) {
commit('CHANGE_SETTING', data)
},
delProgress({commit}, data) {
commit('DEL_PROGRESS', data)
},
setProgress({commit}, data) {
commit('SET_PROGRESS', Object.assign({}, data))
}
}
}
export default app
4.最后在入口文件添加浏览器刷新的监听,再刷新结束后重新发起未下载完成文件的请求,请求结束后清除浏览器的缓存信息及vuex中的信息
/**
* @description 监听浏览器的强刷重新发起断掉的下载请求
*/
window.addEventListener("load", () => {
if(sessionStorage.getItem("downProgress") === null ||
JSON.parse(window.decodeURIComponent(window.atob(sessionStorage.getItem("downProgress"))))?.length < 1) return
const downfile = [].concat(JSON.parse(window.decodeURIComponent(window.atob(sessionStorage.getItem("downProgress")))))
setTimeout(() => {
sessionStorage.removeItem('downProgress')
},150)
downfile.forEach(file => {
if(file.path) {
this.$store.dispatch('setProgress', file)
downFileAgin(file).then(res => {
const blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
if ('download' in document.createElement('a')) {
// 非IE
const elink = document.createElement('a')
elink.download = file.txt_name || '数据列表'
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href)
document.body.removeChild(elink)
} else {
// IE10+
navigator.msSaveBlob(blob, file.txt_name || '数据列表')
}
})
.catch(err =>{
file.func && file.func()
})
}
});
});
/**
* @description 浏览器强刷前把vuex的下载进程存储到session中
*/
window.addEventListener("beforeunload", () => {
sessionStorage.removeItem('downProgress')
if(this.$store.getters.downProgress?.length > 0) {
const filelist = this.$store.getters.downProgress.filter(val => val.path)
sessionStorage.setItem('downProgress', window.btoa(window.encodeURIComponent(JSON.stringify(filelist))))
}
})