参考博文:https://blog.csdn.net/weixin_42682011/article/details/122740407
前端请求后端服务下载安装包等大文件时请求超时,由于文件大小不定无法通过修改超时时间解决;并且文件过大时接口请求时间长,页面无法查看请求进度,用户无法看到是否已经在下载文件。
针对该问题一开始考虑的是由后端给前端一个url地址,前端直接丢给浏览器下载,可以看到浏览器自带的下载进度,对用户相对友好;但出现了新问题,由于项目是前后端分离的项目,存在跨域情况,浏览器下载无法修改文件名(a标签的download属性只在同源有效)。后来又考虑了其他一些方法,都或多或少的存在一些问题,最终选择分片下载。
1、调后端接口函数
export function fileDownload ({ param, bytes }) {
return request({
url: 'url',
method: 'GET',
params: param,
responseType: 'blob',
headers: {
Range: bytes
},
timeout: 60000 // 超时时间
})
}
fileDownload (params) {
return new Promise(resolve => {
// fileDownload为封装的后端接口名
fileDownload(params).then(res => resolve(res.data))
})
},
2、递归函数,分片请求后端接口,提供每次请求的文件流范围,this.byteSize
为每次请求的文件流大小
// 点击下载的函数(递归实现)
downloadFun (i, total, val) {
const requestHandle = async () => {
const params = {
param: '后端需要的参数',
bytes: `bytes=${i * this.byteSize}-${i + 1 >= total ? val.packageSize : (i + 1) * this.byteSize}` // 切片范围,判断是最后一次请求时切片范围是上一次请求的截止值到文件大小
}
const context = await this.fileDownload(params)
val.dataStream.push(context)
val.progress = Number(((++i / total) * 100).toFixed(0)) // 进度的百分比展示,用于页面增加进度条
if (i >= total) {
// 最后一包
this.saveFileDownload(val)
if (type) {
const count = this.chosenPackageList.findIndex(item => {
return item.packageId === val.packageId
})
this.chosenPackageList.splice(count, 1)
// 批量下载时限制同时下载最多3个
if (this.chosenPackageList.length >= 3) {
this.shardToDownload(this.chosenPackageList[2], true)
}
}
} else {
this.$nextTick(() => {
requestHandle()
})
}
}
requestHandle()
},
3、获取到文件流数组后进行下载
// 流文件保存本地并下载
saveFileDownload (val) {
const saveFile = new Blob([...val.dataStream])
FileSaver.saveAs(saveFile, '文件名')
val.dataStream = []
setTimeout(() => {
val.progress = 0
}, 3000)
},
4、每个文件的下载处理,分片处理
shardToDownload (item, type) {
const total = Math.ceil(item.packageSize / this.byteSize) // 计算文件分成几片,调几次接口
const i = 0
item.dataStream = []
this.$set(item, 'progress', 0)
this.downloadFun(i, total, item, type)
},
5、点击下载按钮实现下载,由于项目需要批量下载,这里会有个循环所有下载文件
handleDownload (file) {
if (file) { // 判断是单文件下载还是批量下载
this.shardToDownload(file, false)
} else {
this.chosenPackageList.forEach((item, index) => { // chosenPackageList为选择需要批量下载的文件
if (index < 3) { // 批量下载时限制同时下载最多3个
this.shardToDownload(item, true)
}
})
}
},