JS二進制流文件下載導出(接口返回二進制流)亂碼處理方法
給下載文件起名字有兩種方式:
- 固定值+時間戳(一般都加上時間戳)
- 后端將文件名放到response的headers中返回,我們?nèi)≈诞斪鑫募?/li>
使用react的同學注意了,fetch難設(shè)置亂碼轉(zhuǎn)碼,建議使用axios,用fetch親自嘗試失敗+10086次,下載下來的xlsx全是亂碼
平時在前端下載文件有兩種方式,一種是后臺提供一個 下載的http URL,然后用 window.open(URL)或者創(chuàng)建一個a標簽的href屬性下載,另一種就是后臺直接返回文件的二進制內(nèi)容,然后前端轉(zhuǎn)化一下再下載。
由于第一種方式比較簡單,在此不做探討。本文主要講解一下第二種方式怎么實現(xiàn)。
Blob、ajax(axios)
mdn 上是這樣介紹 Blob 的:
Blob 對象表示一個不可變、原始數(shù)據(jù)的類文件對象。Blob 表示的不一定是JavaScript原生格式的數(shù)據(jù)
具體使用方法
axios({ method: 'post', url: '/export', }) .then(res => { // 假設(shè) data 是返回來的二進制數(shù)據(jù) const data = res.data const url = window.URL.createObjectURL(new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"})) const link = document.createElement('a') link.style.display = 'none' link.href = url link.setAttribute('download', 'excel.xlsx') document.body.appendChild(link) link.click() document.body.removeChild(link) })
打開下載的文件,看看結(jié)果是否正確。
一堆亂碼…
一定有哪里不對。
最后發(fā)現(xiàn)是參數(shù) responseType 的問題,responseType 它表示服務器響應的數(shù)據(jù)類型,由于后臺返回來的是二進制數(shù)據(jù),所以我們要把它設(shè)為 arraybuffer(如果后端傳遞過來約定的是blob,那么responseType應該設(shè)置成這個responseType: 'blob')
接下來再看看結(jié)果是否正確。
axios({ method: 'post', url: '/export', responseType: 'arraybuffer', // responseType: 'blob' }) .then(res => { // 假設(shè) data 是返回來的二進制數(shù)據(jù) const data = res.data const url = window.URL.createObjectURL(new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"})) const link = document.createElement('a') link.style.display = 'none' link.href = url link.setAttribute('download', 'excel.xlsx') document.body.appendChild(link) link.click() document.body.removeChild(link) })
這次沒有問題,文件能正常打開,內(nèi)容也是正常的,不再是亂碼。
根據(jù)后臺接口內(nèi)容決定是否下載文件
作者的項目有大量的頁面都有下載文件的需求,而且這個需求還有點變態(tài)。
具體需求如下
如果下載文件的數(shù)據(jù)量條數(shù)符合要求,正常下載(每個頁面限制下載數(shù)據(jù)量是不一樣的,所以不能在前端寫死)。
如果文件過大,后臺返回 { code: 199999, msg: '文件過大,請重新設(shè)置查詢項', data: null },然后前端再進行報錯提示。
先來分析一下,首先根據(jù)上文,我們都知道下載文件的接口響應數(shù)據(jù)類型為 arraybuffer。返回的數(shù)據(jù)無論是二進制文件,還是 JSON 字符串,前端接收到的其實都是 arraybuffer。所以我們要對 arraybuffer 的內(nèi)容作個判斷,在接收到數(shù)據(jù)時將它轉(zhuǎn)換為字符串,判斷是否有 code: 199999。如果有,則報錯提示,如果沒有,則是正常文件,下載即可。具體實現(xiàn)如下:
axios.interceptors.response.use(response => { const res = response.data // 判斷響應數(shù)據(jù)類型是否 ArrayBuffer,true 則是下載文件接口,false 則是正常接口 if (res instanceof ArrayBuffer) { const utf8decoder = new TextDecoder() const u8arr = new Uint8Array(res) // 將二進制數(shù)據(jù)轉(zhuǎn)為字符串 const temp = utf8decoder.decode(u8arr) if (temp.includes('{code:199999')) { Message({ // 字符串轉(zhuǎn)為 JSON 對象 message: JSON.parse(temp).msg, type: 'error', duration: 5000, }) return Promise.reject() } } // 正常類型接口,省略代碼... return res }, (error) => { // 省略代碼... return Promise.reject(error) })
react fetch(react方案)
const qryurl = `/offapi/liveaum/exportExcelData`; fetch(qryurl, { method: 'POST', credentials: 'include', headers: { Accept: 'application/json', 'Content-Type': 'application/json;charset=UTF-8' }, body: JSON.stringify(params), }).then(response => { if (response.ok) { let downloadFileName = decodeURIComponent(response.headers.get('filename')); this.setState({ downloadFileName, downModalShow: true, iconLoading: false }); response.blob().then(blob => { downBlob = blob; }); } }).catch(function(err) { console.log(err); downBlob = null; message.warning('暫未查詢到數(shù)據(jù)'); this.setState({ iconLoading: false }) }); // 導出文件 handleOk = () => { const link = document.createElement('a'); link.style.display = 'none'; link.href = URL.createObjectURL(downBlob); link.download = `${this.state.downloadFileName}.xls`; document.body.appendChild(link); link.click(); URL.revokeObjectURL(link.href); document.body.removeChild(link); message.success('下載成功'); this.onModalclose(); };
到此這篇關(guān)于JS二進制流文件下載導出(接口返回二進制流)亂碼處理的文章就介紹到這了,更多相關(guān)js二進制文件下載導出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS實現(xiàn)點擊Radio動態(tài)更新table數(shù)據(jù)
這篇文章主要介紹了JS實現(xiàn)點擊Radio動態(tài)更新table數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2017-07-07