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 文件,等下載完畢在作刪除,尚未找到能夠跨過這一步的方式。