如果在项目中第一次遇到下载、导出文件的时候,我们都会直接去请求API,期望会下载一个文件到本地,然后我们可以打开它。但是看到的结果却出乎意料。
接口返回的二进制流
并没有出现期望的情形,而是返回了一堆“乱码”。
AJAX无法下载文件的原因
下载其实是浏览器的内置事件,浏览器的 GET请求(frame、a)、 POST请求(form)具有如下特点:
- response会交由浏览器处理
- response内容可以为二进制文件、字符串等
但是AJAX请求不一样:
- response会交由 Javascript 处理
- response内容只能接收字符串才能继续处理
因此,AJAX本身无法触发浏览器的下载功能。
Axios如何实现下载
- 发送请求
- 获得response
- 通过response判断返回是否为流文件
- 如果是文件则在页面中插入frame/a标签
- 利用frame/a标签实现浏览器的get下载
首先封装一个download方法,用于发送请求
// request.js
import Axios form 'axios';
/*
* @params {string} url 请求地址
* @params {object} resOpts 请求配置参数
*/
const download = (url, resOpts = {}) => {
const { type = 'get', data = '' } = resOpts
const queryArgs = {
url,
method: type,
data,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json; charset=utf-8',
withCredentials: true,
},
}
// tips: 这里直接返回的是response整体!
return Axios.request(queryArgs).catch(err => console.log(err))
}
...
拿到response之后我们需要将流文件通过浏览器下载
// utils.js
export function convertRes2Blob(response) {
// 提取文件名
const fileName = response.headers['content-disposition'].match(
/filename=(.*)/
)[1]
// 将二进制流转为blob
const blob = new Blob([response.data], { type: 'application/octet-stream' })
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// 兼容IE,window.navigator.msSaveBlob:以本地方式保存文件
window.navigator.msSaveBlob(blob, decodeURI(filename))
} else {
// 创建新的URL并指向File对象或者Blob对象的地址
const blobURL = window.URL.createObjectURL(blob)
// 创建a标签,用于跳转至下载链接
const tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = blobURL
tempLink.setAttribute('download', decodeURI(filename))
// 兼容:某些浏览器不支持HTML5的download属性
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank')
}
// 挂载a标签
document.body.appendChild(tempLink)
tempLink.click()
document.body.removeChild(tempLink)
// 释放blob URL地址
window.URL.revokeObjectURL(blobURL)
}
}
缺点
- download请求方法与convertRes2Blob处理文件下载的方法,需要分开调用
- download使用独立的实例,不能公用一个axios,基础配置需要单独维护