痛点是保存服务端返回的二进制数据流文件。
由于之前遇到的都是服务端直接返回文件地址,前端直接 window.open(url)
下载保存的,所以初次遇到文件流保存的,也是很头痛的。
初尝试
直接再用老办法试试?
window.open(res.data)
结果很尴尬,现象是从当前窗口 http://localhost:8083/#/list
新开了一个窗口 http://localhost:8083/[object%20ArrayBuffer]#/list
,并没有下载任何文件。
再尝试
总结之前的初尝试,发现很愚蠢:res.data
是一个二进制文件流,而平时我们使用 window.open()
,参数带的是url
,并不是任何其他格式的数据。
那么,我们接下来考虑,既然服务端返回的是一个二进制流,那么我们前端是不是有什么工具或者方法能够接收并处理这种二进制流的呢?
一番了解,Blob
对象跳入眼中:
Blob
是一种JavaScript
的对象类型,表示一个不可变、原始数据的类文件对象。HTML5
的文件操作对象,其他操作二进制数据的API(比如File对象),都是建立在Blob对象基础上的,继承了它的属性和方法。
完美对接。
let blob = new Blob([res.data], {type: "application/vnd.ms-excel"}); // 将服务端返回的文件流excel文件
let fileName = `资源${new Date().getTime()}.xls`; // 保存的文件名
this.downLoadFile(blob, fileName);
/**
* 下载文件流(兼容IE10)
* @param {*} blob
* @param {*} fileName
*/
downLoadFile (blob, fileName) {
if (window.navigator.msSaveOrOpenBlob) { // IE10
navigator.msSaveBlob(blob, fileName);
} else {
let link = document.createElement('a');
link.style.display = 'none';
link.href = URL.createObjectURL(blob); //创建一个指向该参数对象的URL
link.download = fileName;
link.click(); // 触发下载
URL.revokeObjectURL(link.href); // 释放通过 URL.createObjectURL() 创建的 URL
}
}
结果还可以,文件成功下载了(base64
文件下载也可参照上述逻辑哦)。
但是,乱码了。
WTF,我要稳住,胜利近在咫尺。
三尝试
总结再尝试,现在的情况是文件能下载了,文件名也是我们正确的按我们指定的保存的,美中不足则是文件乱码。
"嘿嘿嘿,二话不说,肯定是服务端的锅。" 脑子里一直回荡着这句话,在确保了自己是 UTF-8
接收的后,找后端小哥理论去了。结果很尴尬,小哥断点保存后打开居然是正确的格式,现在压力来到了我这边。
服务端明确表示返回的是一个二进制数据流,我们前端接收也使用了 Blob
,按理说是完美对接了,唯一有可能出错的地方就是前端从服务端获取到的数据类型并不是 Blob
了,而从之前找后端小哥打脸的经历我们可以确定服务端的response
内容是没有问题的,一番分析,确定了问题出在前端接收 ajax
上了。
果然,ajax
中有一个responseType
的属性。
XMLHttpRequest.responseType
属性是一个枚举值,返回响应的类型。允许作者将响应类型更改为arraybuffer
,blob
,document
,json
, 或text
。
当然,这个值也不是瞎设的,当我们将 responseType
设置为一个特定的类型时,我们要确保服务器所返回的类型和你所设置的返回值类型是兼容的,不然的话,尽管有再多数据返回,也都成了 null
。
我们再试:
终于成功了。
总结
涉及到二进制数据流操作的,我们使用Blob
对象。
与服务端交互的,我们切记要设置responseType
。