用 axios 发送请求下载文件请求后端会出现以下问题,返回的是二进制流格式,如图所示:
比较纳闷,后来网上查了各种资料才知道,前端需要 blob 就可以将二进制流文件下载到本地了。
实现思路
- 在页面新建 a 标签 或者按钮点击事件;
- 创建 Blob 对象,在 Blob 中传入后端返回的 response.data,这一步中 Blob 需要的是一个数组类型的参数, 后端二进制流这边接收到的 response.data 使用查看发现是String, 所以我把 response.data 放进一个长度1的数组, 再传入 Blob 对象,此外需要规定文件类型, 可以是 doc 的
application/vnd.openxmlformats-officedocument.wordprocessingml.document
, 也可以是二进制流的application/actet-stream
,但是我在项目中并没有指定,也是可以的。 - 获取下载文件名字,并使用 decodeURI() 函数进行解析;
- 创建下载链接 window.URL.createObjectURL();
- 把步骤四创建的链接变量赋值给 a 标签的 href 属性;
- 使用 document.body.appendChild() 方法把 a 标签挂上去,再调用 a 标签的 .click() 事件;
- document.body.removeChild() 移除 a 标签;
- window.URL.revokeObjectURL() 释放 blob 对象。
备注
上面步骤三的操作,从响应头里 content-disposition
属性可以获取到文件名,下面代码也给出了解决方案,当然前提是后端接口是要设置这个属性的。
代码
axios({
method: 'post',
data: parameter,
url: `/download`,
headers: {
'Content-Type': 'application/json',
},
responseType: 'blob'
}).then(res => {
let blob = new Blob([res.data])
let contentDisposition = res.headers['content-disposition']
let pattern = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
let result = pattern.exec(contentDisposition)
// 使用decodeURI对名字进行解码
let fileName = decodeURI(result[1])
let downloadElement = document.createElement('a')
// 创建下载的链接
let href = window.URL.createObjectURL(blob)
downloadElement.style.display = 'none'
downloadElement.href = href
// 下载后文件名
downloadElement.download = fileName
document.body.appendChild(downloadElement)
// 点击下载
downloadElement.click()
// 下载完成移除元素
document.body.removeChild(downloadElement)
// 释放掉blob对象
window.URL.revokeObjectURL(href)
}).catch(err => {
console.log('下载失败')
})