在上一次讲过项目需求下载zip文件格式,还没看的小伙伴可以先看一下:
这次亲爱得甲方又加需求了,他们希望在下载得同时,可以查看到下载进度,因为伴随这下载内容得增大,没有明确得下载进度,用户体验很差! 这次依旧是用axios和fetch两种方法实现下载进度表
axios
获取下载进度 需要让后端设置好响应头 也就是’ContentLength’必不可少,他是后端返回得文件大小,这是要计算下载进度非常重要得一环!!但还是要注意main.ts是否存在mock导入,一旦有下载后的文件全是被破坏的!!!
利用axios封装的原生onDownloadProgress属性,该属性为一个回调方法,当axios请求将文件从服务器下载时会进行回调。接收一个回调参数,该回调参数中包含总当前下载量,总的下载量。
axios({
method: 'POST',
url,
data: params,
responseType: 'blob', //告诉服务器想到的响应格式
headers: { 'Content-Type': 'application/json; application/octet-stream' },
//onDownloadProgress方法!!! 其他和之前没啥变化 并赋值给percentage用作之后得回显
onDownloadProgress: progressEvent => {
this.percentage = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
console.log(this.percentage);
},
})
.then(res => {
let blob = new Blob([res.data], { type: 'application/zip' }); //设置下载的内容以及格式
const url = window.URL.createObjectURL(blob); //设置路径
const link = window.document.createElement('a'); // 创建a标签
link.href = url;
link.download = '文件'; //设置文件名
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url); // 释放内存
})
.catch(function (error) {
showMessage('下载失败', 'error');
console.log(error);
});
在得到下载进度后利用element-ui库中得 Notification 通知属性
代码如下:
this.notify:any = {};
//开始下载时等待服务器响应数据 利用this.notify获得通知框得实例 方便动态回显
this.notify = this.$notify({
title: '下载进度',
offset: 130,
dangerouslyUseHTMLString: true,
duration: 0,
message: `<p style="width: 200px;">正在准备数据,请等待</p>`,
customClass: 'notify-download',
showClose: false,
});
//之后需要监听percentage数值得变化 改变通知框得数值回显
//这里watch思路写的有点乱,因为后面需求和一开始有变化,就采取了添油战术,懒得优化
@Watch('percentage')
watchPercentage() {
if (this.percentage === 100) {
this.notify.message = `<p style="width: 100px;">下载完成<span style="float: right">${this.percentage}%</span></p>`;
this.notify.showClose = true;
setTimeout(() => {
this.notify.close();
}, 2000);
} else if (this.percentage > 0) {
this.notify.message = `<p style="width: 100px;">正在下载<span style="float: right">${this.percentage}%</span></p>`;
} else {
this.notify.message = `<p style="width: 200px;">正在准备数据,请等待</p>`;
}
}
笔者这里之所以和正常vue的watch不同是因为 笔者所在的项目是vue2+ts需要引入特殊的vue库,大体思路没问题就好。
fetch
fetch没有自带的属性 写起来很麻烦,唯一的好处就是不用但main.ts里的mock影响到他吧- -
let size = 0;
fetch(url, {
body: JSON.stringify(params),
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
.then(res => {
if (res.ok) {
return res;
} else {
showMessage('请求下载失败', 'error');
}
})
.then(res => {
if (res) {
this.downloadLength = res.headers.get('Content-Length');
return res.body;
}
})
.then(body => {
if (body) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let this_ = this;
const reader = body.getReader();
return new ReadableStream({
start(controller) {
return pump();
function pump(): any {
return reader.read().then(res => {
//res ({ done, value })
// 读不到更多数据就关闭流
const { done, value } = res;
if (done) {
controller.close();
}
size += value ? value.length : 0;
this_.percentage = ((this_.downloadLength / size) * 100) | 0;
// 将下一个数据块置入流中
controller.enqueue(value);
return pump();
});
}
},
});
}
})
.then(stream => new Response(stream))
.then(res => {
res.blob().then(blob => {
// 转化为blobURL后再通过a标签下载
const blobUrl = window.URL.createObjectURL(blob);
const a = window.document.createElement('a');
a.href = blobUrl;
a.download = '文件.zip';
a.click();
window.URL.revokeObjectURL(blobUrl);
});
})
.catch(err => {
showMessage('下载失败', 'error');
});
使用fetch后 还是使用 element-ui库中得 Notification 通知属性 ,代码和上面的一样
总结
axios有封装好的回调函数,而fetch没有所以要逐层逐层回调地狱去找到 最后在用过递归函数获得当前下载量,很麻烦 ,推荐使用axios,但在使用中切记main.ts里是否存在mock!