痛點是保存服務端返回的二進制數據流文件。
由于之前遇到的都是服務端直接返回文件地址,前端直接 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
。