前端下載二進制流文件


平時在前端下載文件有兩種方式,一種是后台提供一個 URL,然后用 window.open(URL) 下載,另一種就是后台直接返回文件的二進制內容,然后前端轉化一下再下載。

由於第一種方式比較簡單,在此不做探討。本文主要講解一下第二種方式怎么實現。

Blob、ajax(axios)

mdn 上是這樣介紹 Blob 的:

Blob 對象表示一個不可變、原始數據的類文件對象。Blob 表示的不一定是JavaScript原生格式的數據

具體使用方法

axios({
  method: 'post',
  url: '/export',
})
.then(res => {
  // 假設 data 是返回來的二進制數據
  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)
})

打開下載的文件,看看結果是否正確。

在這里插入圖片描述

一堆亂碼...

一定有哪里不對。

最后發現是參數 responseType 的問題,responseType 它表示服務器響應的數據類型,由於后台返回來的是二進制數據,所以我們要把它設為 arraybuffer
接下來再看看結果是否正確。

axios({
  method: 'post',
  url: '/export',
  responseType: 'arraybuffer',
})
.then(res => {
  // 假設 data 是返回來的二進制數據
  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)
})

在這里插入圖片描述

這次沒有問題,文件能正常打開,內容也是正常的,不再是亂碼。

根據后台接口內容決定是否下載文件

作者的項目有大量的頁面都有下載文件的需求,而且這個需求還有點變態。

具體需求如下

  1. 如果下載文件的數據量條數符合要求,正常下載(每個頁面限制下載數據量是不一樣的,所以不能在前端寫死)。
  2. 如果文件過大,后台返回 { code: 199999, msg: '文件過大,請重新設置查詢項', data: null },然后前端再進行報錯提示。

先來分析一下,首先根據上文,我們都知道下載文件的接口響應數據類型為 arraybuffer。返回的數據無論是二進制文件,還是 JSON 字符串,前端接收到的其實都是 arraybuffer。所以我們要對 arraybuffer 的內容作個判斷,在接收到數據時將它轉換為字符串,判斷是否有 code: 199999。如果有,則報錯提示,如果沒有,則是正常文件,下載即可。具體實現如下:

axios.interceptors.response.use(response => {
    const res = response.data
    // 判斷響應數據類型是否 ArrayBuffer,true 則是下載文件接口,false 則是正常接口
    if (res instanceof ArrayBuffer) {
        const utf8decoder = new TextDecoder()
        const u8arr = new Uint8Array(res)
        // 將二進制數據轉為字符串
        const temp = utf8decoder.decode(u8arr)
        if (temp.includes('{code:199999')) {
            Message({
            	// 字符串轉為 JSON 對象
                message: JSON.parse(temp).msg,
                type: 'error',
                duration: 5000,
            })

            return Promise.reject()
        }
    }
    // 正常類型接口,省略代碼...
    return res
}, (error) => {
    // 省略代碼...
    return Promise.reject(error)
})

更多文章,敬請關注


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM