Content-Disposition / Content-Type
Content-Disposition
http 頭部的 Content-Disposition欄位,規定了返回的內容用什麼形式展示
value | 含義 | 是否預設 |
---|---|---|
inline | 以網頁或者頁面的一部分 | 是 |
attachment | 以附件的形式下載並儲存到本地 | 否 |
http.createServer((req, res) => {
res.setHeader("Content-Disposition", "attachment")
res.end('123 - 321 - 1234567')
})
複製程式碼
前端需要使用window.open 形式訪問 此路由就可以實現檔案的下載
window.open(xxxx)
複製程式碼
或者使用 H5新屬性 a 標籤
<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標籤進行處理
備註: 如果使用普通的請求,是不可以的,比如使用ajax
window.open
window.open 可以下載檔案的原因是,瀏覽器遇到無法解析的檔案就是執行下載
當使用瀏覽器開啟檔案時, 如果它無法解析,那麼就會把該檔案下載下來
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 檔案,等下載完畢在做刪除,還沒有找到可以跨過這一步的方式。