后端直接返回字节流
this.request(Object.assign({ responseType: 'blob' }, options), params).then((res) => {
if (!res?.data) {
message('未能获取文件流')
return Promise.reject()
}
try {
let contentDisposition = res.headers['content-disposition'].replace(/['"]/g, '')
// 文件名需要url编码,才能读取,否则取到的是乱码
let patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
let result = patt.exec(contentDisposition)
let filename = decodeURI(result[1])
const url = URL.createObjectURL(new Blob([res.data]))
const a = document.createElement('a')
a.style.display = 'none'
a.href = url
a.setAttribute('download', filename)
if (typeof a.download === 'undefined') {
a.setAttribute('target', '_blank')
}
document.body.appendChild(a)
a.click()
// 启动下载后需要释放资源
document.body.removeChild(a)
URL.revokeObjectURL(url)
} catch (error) {
console.log(error)
message('可能浏览器版本低,请升级或更换浏览器后重试')
return Promise.reject()
}
return Promise.resolve()
})
注意事项
- responseType要和headers同级,不能放在headers里
- 后端需要设置
content-disposition
并且文件名要带后缀 - 每次结束一定要调用
URL.revokeObjectURL
,避免内存泄漏
后端返回字节流被base64编码
当后端不是以字节数组的形式直接塞入response时,字节数组会自动转为base64
this.request(Object.assign({ responseType: 'blob' }, options), params).then((res) => {
try {
const filename = res.data.filename
const base64Str = atob(res.data.file) // res.data.file为base64字符串
const len = base64Str.length
const bytes = new Int8Array(len)
for (let i = 0; i < len; i++) {
bytes[i] = base64Str.charCodeAt(i)
}
const url = URL.createObjectURL(new Blob([bytes]))
const a = document.createElement('a')
a.style.display = 'none'
a.href = url
a.setAttribute('download', filename)
if (typeof a.download === 'undefined') {
a.setAttribute('target', '_blank')
}
document.body.appendChild(a)
a.click()
// 启动下载后需要释放资源
document.body.removeChild(a)
URL.revokeObjectURL(url)
} catch (error) {
console.log(error)
message('可能浏览器版本低,请升级或更换浏览器后重试')
return Promise.reject()
}
return Promise.resolve()
})