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>
複製代碼
http.createServer((req, res) => { res.setHeader("Content-Type", "application/octet-stream") res.end('123 - 321 - 1234567') }) 複製代碼
同上前端使用 open 或者 a標籤進行處理ios
備註: 若是使用普通的請求,是不能夠的,好比使用ajaxajax
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 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 是表示一個類文件對象,能夠用它來表示一個文件
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 =>{ ... }) 複製代碼
createObjectURL
用 blob 對象來建立一個 object URL(它是一個 DOMString),咱們能夠用這個 object URL 來表示某個 blob 對象,這個 object URL 能夠用在 a 標籤的 href屬性上,而後觸發點擊事件,就能夠下載文件了
revokeObjectURL
爲了不避免內存泄漏,須要手動釋放建立的 object URL
從代碼就能看出,須要先將返回內容轉爲blob才進行下載操做,若是用戶操做的是一個很大的資源,在等待文件正式下載前,還須要一段時間等待格式的轉化
這邊直接用數據模擬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 下載方式了
如圖,使用wps打開會被展現xxxE+15
開發時候使用Mac自帶的numbers是沒有問題的,沒有發現換一個軟件就不行了,處理方案從網絡上查到的,給過長的數據添加一個 /t 就能夠了 [使用 ' 半角單引號 或者 \t 均可以]
result.id = `${id}\t` 複製代碼
其實這個問題是在查詢資料的過程有人提到的問題
批量寫了多個 window.open 在Chrome中 能夠正常使用,可是在safari中,只能打開一個窗口,下載一個文件
感受若是將全部文件合併成爲一個xxx.zip 而後對 這個zip文件作下載。不過這種方式有問題,目前查到的大部分過程都是會在服務器新建出一個 zip 文件,等下載完畢在作刪除,尚未找到能夠跨過這一步的方式。