下載附件(image,doc,docx, excel,zip,pdf),應該是實際工作中經常遇到一個問題;這里使用過幾種方式分享出來僅供參考; 初次寫可能存在問題,有問題望指出
主要了解的幾個知識點:
-
http 響應頭設置
- Content-Disposition
- Access-Control-Expose-Headers 這里只需要涉及跨域的時才使用,用於暴露JavaScript中能夠獲取到響應頭字段
- Blob 、 FileReader
- URL
先來介紹常用方式: 這里下載.doc文檔為例,其它都類似
利用 iframe 或 a 連接
服務端代碼
// nodejs
const http = require('http');
const fs = require('fs');
const path = require('path');
http.createServer(function (req, res) {
let filename = encodeURIComponent('微信多開的步驟.doc');
// 下面兩個主要在跨域情況下,需要設置的
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
// 設置響應頭
res.setHeader('Content-Type', 'application/zip;charset=UTF-8');
res.setHeader('Content-Disposition', `attachment; filename=${filename}`);
let fs.readFile(path.resolve(__dirname, `./微信多開的步驟.doc`), function (err, data) {
if (err) throw err;
res.end(data);});
}).listen(3000);
Content-Disposition 消息頭指示回復的內容該以何種形式展示,是以內聯的形式(即網頁或者頁面的一部分),還是以附件的形式下載並保存到本地。
大概流程:
1 下載時瀏覽器會嘗試去找下響應頭中 Content-Disposition ;
2 如果不存在,首先嘗試去預覽方式打開該文件 ,如果能就直接顯示否則以附件的形式下載並保存;
注意:指定在下載文件名中文情況下,必須先進行編碼;
JS
// iframe
var downloadFileUrl = "http://localhost:3000"
var elemIF = document.createElement("iframe");
elemIF.src = downloadFileUrl;
elemIF.style.display = "none";
document.body.appendChild(elemIF);
// a
var a = document.createElement('a');
a.href = downloadFileUrl;
a.click();
上述兩種方式僅僅就是發送一個請求,主要依賴后端的支持;對不需要精確知道文件下載的狀態,上面方式就能滿足下載;
大家可能有疑問,iframe 不是可以通過 onload 來捕獲加載的完成狀態 ?
先來看看 load 適用哪些對象?
load
W3C 對 load 定義
Type load Sync / Async Async Bubbles No Trusted Targets Window,Document,Element
適用對象:window,Document,Element 那么對於我們下載的文件並在其范圍;
如果需要捕獲文件下載的進度以及文件下載完成的狀態,需要使用下面的方式;
XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onprogress = function (event) {
console.log(Math.round(event.loaded / event.total * 100) + "%");
};
xhr.responseType = 'arraybuffer';
xhr.addEventListener('readystatechange', function (event) {
if (xhr.status === 200 && xhr.readyState === 4) {
// 獲取響應頭主要獲取附件名稱
var contentDisposition = xhr.getResponseHeader('content-disposition');
// 獲取類型類型和編碼
var contentType = xhr.getResponseHeader('content-type');
// 構造blob對象,具體看頭部提供的鏈接地址
var blob = new Blob([xhr.response], {
type: contentType
});
var url = window.URL.createObjectURL(blob);
// 獲取文件夾名
var regex = /filename=[^;]*/;
var matchs = contentDisposition.match(regex);
if (matchs) {
filename = decodeURIComponent(matchs[0].split("=")[1]);
} else {
filename = +Date.now() + ".doc";
}
var a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
// dosomething
}
})
xhr.send();
上述對比第一種方式,通過 onprogress 捕獲下載進度(界面通過顯示進度條來提升體驗);通過 readystatechange 監聽下載完后並可以做其它的事情;
注意: 必須指定 responseType 類型,可以是arraybuffer 或 blob 否則會出現錯誤問題 比如 zip,pdf文件下載之后打不開提示錯誤的格式; .doc,.excel文件內容亂碼等;
