业务场景
老生常谈的前端导出文件功能,由于导出接口所需要的参数较多,并且get请求对于url的长度有限制,所以选择用post请求来实现交互,完成功能,这里也顺便说一下get请求。
get请求:
## 这里的url包含(服务域名地址+服务端提供的导出接口+导出接口所需的业务参数)
// 方法1
window.open(url, _self)
// 方法2
<a href="url" download="filename">点击链接下载</a>
// 方法3
window.location.href = url
这种方式简单快捷,但是最好使用https的域名,不然浏览器会报警告信息。
post请求:
post请求,由于服务端返给前端的是流文件数据。我们需要去封装一个接收流文件的请求,如下:
# 接收流文件
export function exportPost(url, data) {
return new promise((resolve, reject) => {
axios.post(url, data, {
responseType: 'blob',
headers: {
'Content-Type': 'application/json;charset=UTF-8' // 默认是Json 可根据需求进行自定义
}
})
}).then(res => {
resolve(res)
}).catch(error => {
reject(error)
})
}
完成这步之后我们还需要在业务代码里封装转换流文件的工具函数,如下:
// 工具函数
function exportUtils(data, name) {
let url = window.URL.createObjectURL(data) //表示一个指定的file对象或Blob对象
let a = document.createElement('a')
document.body.appendChild(a)
a.href = url
//filename名称截取(后端拼到了header头里了)
let fileName = data.headers['content-disposition'].split(';')[1].split('=')[1]
a.download = decodeURI(fileName) // 命名下载名称,文件名解码
a.click() //点击触发下载
window.URL.revokeObjectURL(url)
}
如上函数,前端也可以通过name参数自定义导出的文件名;因为业务需求这里服务端直接将文件名封装到接口响应头里了,前端只需去响应头里去获取content-disposition的属性值作为导出文件的名称。
问题来了
- 后端已经将content-disposition字段和属性值添加到导出接口的响应头里了,但是前端访问之后获取到的响应体里并没有content-disposition字段。
- content-disposition属性值中文乱码。(这个需要后端去解决,我这里也聊一聊)
分析问题
问题1:
由于第一次使用这种方式获取导出文件名,查阅了MDN文档后;原因是浏览器默认情况下对服务端暴露给客户端的响应体内容是有限制的,后端可以通过设置Access-Control-Expose-Headers这个属性来控制暴露给客户端的响应内容,但是默认情况下暴露字段并不包括content-disposition,如果想要暴露必须显性的设置它。(阅读者建议研读下这两篇MDN文档Access-Control-Expose-Headers - HTTP | MDN响应标头 Access-Control-Expose-Headers 允许服务器指示那些响应标头可以暴露给浏览器中运行的脚本,以响应跨源请求。https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Expose-Headers 和 响应标头(Response header) - MDN Web 文档术语表:Web 相关术语的定义 | MDN响应标头(response header)是一种 HTTP 标头,其可以用于 HTTP 响应,且与响应消息主体无关。像 Age、Location 或 Server 都属于响应标头,它们被用于提供更详细的响应上下文。
https://developer.mozilla.org/zh-CN/docs/Glossary/Response_header)
问题2:
问题1解决之后,问题2接踵而至,后端通过各种转码方式但是中文文件名在响应体里始终是乱码,最后通过查阅文档在如下两篇文档中找到了答案,HTTP协议header中Content-Disposition中文文件名乱码 - 思创斯聊编程HTTP协议header中Content-Disposition中文文件名乱码在做文件下载时,当文件名为中文时,经常会出现乱码现象。参考文章:http://blog.robotshell.org/2012/deal-with-http-header-encoding-for-file-download/本文就详细给出案例来解决这一乱码问题,以及还一直未解决的一个疑问,欢迎大家一起来探讨。大体的原因就是header中只支持ASCII,所以我们传输的文件名必须是ASCI...https://ispacesoft.com/146787.html和
针对问题2,主要原因其实是编码方式问题,我们上面采用的url编码,后端返回编码后的内容,前端只需要通过decodeURI或者decodeURIComponent进行解码就行了。
注意:前后端的编码方式务必统一,推荐使用url编码。
总结
以上是实现前端导出文件功能的小小总结,希望能够帮到遇到同样问题的小伙伴,如果在阅读中发现问题或者异议,欢迎评论,我及时改正更新。