文件下載那點事

Content-Disposition / Content-Type

Content-Disposition

http 頭部的 Content-Disposition字段,規定了返回的內容用什麼形式展現html

value 含義 是否默認
inline 以網頁或者頁面的一部分
attachment 以附件的形式下載並保存到本地
http.createServer((req, res) => {
  res.setHeader("Content-Disposition", "attachment")
  res.end('123 - 321 - 1234567')    
})
複製代碼

前端須要使用window.open 形式訪問 此路由就能夠實現文件的下載前端

window.open(xxxx)
複製代碼

或者使用 H5新屬性 a 標籤mysql

<a type='download' href=xxx> 點擊下載 </a>
複製代碼

Content-Type

http.createServer((req, res) => {
  res.setHeader("Content-Type", "application/octet-stream")
  res.end('123 - 321 - 1234567')    
})
複製代碼

同上前端使用 open 或者 a標籤進行處理ios

備註: 若是使用普通的請求,是不能夠的,好比使用ajaxajax

window.open

window.open 能夠下載文件的緣由是,瀏覽器遇到沒法解析的文件就是執行下載sql

當使用瀏覽器打開文件時, 若是它沒法解析,那麼就會把該文件下載下來axios

http.createServer((req, res) => {
  const data = fs.readFileSync('./Zip.zip')
  res.end(data)    
})
複製代碼

拿到的data是一個buffer 對象,那麼直接寫一個Buffer能夠實現下載麼數組

data = new Buffer('我是誰,誰是我')
複製代碼

嘗試後發現,koa是能夠的。可是原生直接這麼寫是不行的瀏覽器

http.createServer((req, res) => {
  const newBuf = new Buffer('我是誰,誰是我')
  res.end(newBuf)    
})
複製代碼

經過接口拿到數據以後進行下載

在實際項目中,文件下載可能出現的場景服務器

  • 對於已經在服務器存在的文件進行下載,好比圖片資源

  • 將一些查詢數據導出到本地, 好比mysql查詢結果導出csv

對於已存在的資源,能夠直接使用 上面說的 winodw.open 或者 a 標籤進行下載,那麼對於不是以文件形式存在的資源呢

data URI

常常見到使用 data URI scheme 的是圖片, 通常爲了減小http請求,會將圖片直接以base64的形式展現在html中

<img src='data:image/png;base64,xxxxxx'/>
複製代碼

也能夠應用到文件下載中

接口

router.get('/download', async (ctx, next) => {
  const newBUf = new Buffer('我是誰,誰是我')
  ctx.body = newBUf
}
複製代碼

前端

axios.get(`${path}`)
.then(function ({data}) {
  let a = document.createElement('a');
  a.href = "data:text/plain;charset=utf-8," + data;
  a.download = "myfilename.png";
  a.click();
})
複製代碼

Blob

Blob 是表示一個類文件對象,能夠用它來表示一個文件

server 部分

http.createServer((req, res) => {
  res.setHeader("Access-Control-Allow-Origin", "*");
  let end = ''
  if (req.url.includes("/down")) {
    const newBUf = new Buffer('我是誰,誰是我')
    end = newBUf
  }
  res.end(end)
}  
複製代碼

前端

const xhr = new XMLHttpRequest();
    xhr.open('GET', path);
    xhr.responseType = 'blob';

    xhr.onload = function () {
      const blob = xhr.response;
      const url = URL.createObjectURL(blob);
      // 經過a標籤去下載
      const link = document.createElement('a');
      link.href = url;
      link.download = fileName;
      link.click();
      
      URL.revokeObjectURL(url);
    };
   xhr.send();
複製代碼

嘗試用 window.open 方式,發現不能夠

目前經常使用的各個請求庫也支持返回內容爲blob格式

axios.get(`${path}`, {
    responseType: 'blob'
  })
複製代碼

或者 fetch

fetch(path).then(res => res.blob().then(blob =>{ ... })
複製代碼

window.URL

  • createObjectURL

    用 blob 對象來建立一個 object URL(它是一個 DOMString),咱們能夠用這個 object URL 來表示某個 blob 對象,這個 object URL 能夠用在 a 標籤的 href屬性上,而後觸發點擊事件,就能夠下載文件了

  • revokeObjectURL

    爲了不避免內存泄漏,須要手動釋放建立的 object URL

缺點

  • 構建完 blob 對象後纔會轉換成文件

從代碼就能看出,須要先將返回內容轉爲blob才進行下載操做,若是用戶操做的是一個很大的資源,在等待文件正式下載前,還須要一段時間等待格式的轉化

實例

將mysql數據 導出 csv

這邊直接用數據模擬mysql查詢結果, 在網上查到兩種拼接方式,確定有其他的方案

  • 第一種
const result = [
  ['id', 'oreder', 'name', 'status' ],
  [1, '201904120201', '正在加載', '已完成'],
  [18, '201904120204', '測試189', '待付款'],
  [22, '201904120209', '藍田日暖', '待付款'],
]

// 能夠看到 result 數組的第一組是 csv的各個字段標題 

const data = csvData.reduce((cur, next) => `${cur + next.join(',')}\n`, '')
const blob = new Blob([data]);
const url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
複製代碼
  • 第二種
const result = [
  [1, '201904120201', '正在加載', '已完成'],
  [18, '201904120204', '測試189', '待付款'],
  [22, '201904120209', '藍田日暖', '待付款'],
]

// result 就是正常的數據格式

  // 解決亂碼問題
  let dataType = '\uFEFF'
  // 添加表格的頭子段
  dataType += ([' 訂單編號', '用戶', '動態ID'].join(',')) 
  dataType += '\n'

  result.forEach(item => { 
    // dataType += ([item.id, item.order, item.name, item.staus].join(','))
    dataType += (item.join(','))
    dataType += '\n'
  })

  const blob = new Blob([dataType], {type: 'text/csv'})    
複製代碼

若是有幾段流文件,前端須要下載拼接成一個完整文件,如何實現

確定是在 server端進行文件的拼接操做,而後返回到前端下載

大體過程

const content1 = '這裏是第1段文件內容'

const content2 = '這裏是第2段文件內容'

ctx.body = { code:200, data: content1 + content2 }
複製代碼

前端就是上文中的blob 下載方式了

下載的csv打開數字展現有問題

如圖,使用wps打開會被展現xxxE+15

開發時候使用Mac自帶的numbers是沒有問題的,沒有發現換一個軟件就不行了,處理方案從網絡上查到的,給過長的數據添加一個 /t 就能夠了 [使用 ' 半角單引號 或者 \t 均可以]

result.id = `${id}\t`
複製代碼

批量下載如何處理

其實這個問題是在查詢資料的過程有人提到的問題

  • window.open

批量寫了多個 window.open 在Chrome中 能夠正常使用,可是在safari中,只能打開一個窗口,下載一個文件

  • 轉爲下載一個zip 文件

感受若是將全部文件合併成爲一個xxx.zip 而後對 這個zip文件作下載。不過這種方式有問題,目前查到的大部分過程都是會在服務器新建出一個 zip 文件,等下載完畢在作刪除,尚未找到能夠跨過這一步的方式。

參考文章

相關文章
相關標籤/搜索