值得注意
下載文件,清除緩存策略(最好后端接口處理,前端處理有些瀏覽器不識別)
后端: 接口再頭部添加清除緩存標記
前端: 請求連接后面添加時間戳或者隨機數a標簽下載文件,base64 url文件過大無法下載問題;可以將文件存入內存createObjectURL中,用虛擬內存地址指向
總結
- 所有情況通用的方式: 后端設置下載請求的響應頭 Content-Disposition: attachment;
- filename="filename.jpg"
- attachment 表示讓瀏覽器強制下載
- filename 用于設置下載彈出框里預填的文件名
- 非跨域情況下 給a標簽加上 download 屬性,如 <a href="url" download="xxx.png"></a>
- download 里寫文件名 注意后綴 (值非必填)
- 通過請求解決跨域問題 動態創建a標簽通過blob形式下載 具體看下面解析
- 文件下載通常有以下3種方式
1、<a href="http://localhost:8080/upload/user.png">下載</a> a標簽訪問文件地址
2、window.open('http://localhost:8080/upload/user.png') 打開文件地址
3、后端提供一個接口 /api/download 通過接口返回文件流
瀏覽器通過請求頭Content-Type中的MIME類型(媒體類型,通常稱為 Multipurpose
Internet Mail Extensions 或 MIME 類型,如 :image/jpeg application/pdf)識別數據類型,對相應的數據做出相應處理,對于圖像文本等瀏覽器可以直接打開的文件,默認處理方式就是打開,為了避免瀏覽器直接打開文件我們需要做一些處理;
此方式最為簡易,只需要知道文件在服務器上的地址,就可以通過a標簽實現下載
<a href="https://.../158ac1e6917445a4aa384a2a7209445a.xlsx" download="test">下載文件</a>
<a href="https://.../6d0e6934246c4ba9ba1a43c6992836ca.png" download="test">下載圖片</a>
已知文件的地址,可以通過上面的方式將地址放入href屬性內,download屬性存放下載文件的名稱,此屬性為必須。
若文件地址為異步獲取,即點擊下載/導出按鈕時才會從接口拿,則可以通過js插入a標簽來實現。demo如下:
異步獲取文件路徑之后執行以下代碼即可自動下載
*以下方式都可以避免瀏覽器直接打開文件
一、無監聽下載實現
1、使用a標簽下載 (單個下載)
須知: 當url是同源(同域名、同協議、同端口號)時,這種情況用 a標簽加download屬性的方式即可,download屬性指示瀏覽器該下載而不是打開該文件,同時該屬性值即下載時的文件名
<el-button type="text" @click.stop="downModel">下載模板</el-button>
// 下載模板
downModel() {
let script = document.createElement('a');
script.setAttribute('href',
baseUrl+"/highStarHotel/download/importEmployeeTemplates/v1?
token="+Cookie.get("HOTEL-MA-TOKEN"));
script.setAttribute('target', '_blank');
script.setAttribute("download", "批量導入酒店員工信息模塊.xlsx");
document.body.appendChild(script);
script.click();
document.body.removeChild(script);
},
2、使用iframe 標簽下載(批量下載下載)
downModel() {
// this.downUrlMenu 批量下載鏈接
_.each(this.downUrlMenu, (col) => {
let iframe = document.createElement("iframe");
iframe.setAttribute('style','display:none;height:0')
iframe.setAttribute('src',col)
document.body.appendChild(iframe);
setTimeout(()=>{
document.body.removeChild(iframe);
},2000)
})
}
3、使用form表單下載
/**
* 導出到本地
*
*/
createSelectedForm(url,params) {
let form = $("<form/>")
form.attr("target", "_blank").attr("method", "get").attr("action", url)
_.each(params, (value, key) => {
value = value ? value : ""
form.append("<input type='hidden' value='"+value+"' name='"+key+"' />")
})
form.appendTo(document.body)
form.submit()
form.remove()
}
二、 監聽下載完成實現
實現原理:通過XMLHttpRequest發送請求
- 解決不同瀏覽器的下載觸發navigator.msSaveBlob
- 利用FileReader來讀取文件內容,關鍵是設置a標簽,將blob的文件內容轉換為base64并放入a標簽的href中,模擬點擊來進行下載。
- 對于非常小的文件,比如小圖標等可以用base64作為url地址,對于其他的大文件,可以將文件存入內存中createObjectURL,用虛擬內存地址指向。
URL.createObjectURL 將blob形式的文件存入并返回一個url以供下載。
而URL.revokeObjectURL則將該內存釋放。 - 至此,可以看到大文件根本不能使用base64,直接使用blob是最好的
- Content-Type 需與下載指定類型一致,否則亂碼;https://www.runoob.com/http/http-content-type.html
exportExcel(type) {
let _that = this,
downWord =''公區查驗(復驗)報告.docx',
url = "/ydyf_stat/v1/fb/ci_type_stat/export_issue_report/",
params = {
type:type
},
xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type"
, "application/x-www-form-urlencoded"); // 下載文件類型 .docx
xhr.responseType = "blob"; // 返回類型blob
// 定義請求完成的處理函數
xhr.onload = function () {
// 請求完成 // 返回200
if (this.status === 200) {
let blob = this.response;
try {
let jsonData = JSON.parse(blob);
if (jsonData.code) {
// 說明是普通對象數據,后臺轉換失敗
// to do something
}
} catch (err) {
// 解析成對象失敗,說明是正常的文件流
let fileName = decodeURIComponent(downWord);
if (navigator.msSaveBlob == null) {
let a = document.createElement('a');
a.download = fileName;
a.href = URL.createObjectURL(blob);
$("body").append(a); // 修復firefox中無法觸發click
a.click();
URL.revokeObjectURL(a.href);
$(a).remove();
} else {
navigator.msSaveBlob(blob, fileName);
}
}
}
};
// 發送ajax請求
xhr.send(qs.stringify(params))
},
注意
- axios設置responseType=blob導出文件和失敗返回json處理
axios設置instance.defaults.responseType = 'blob’請求下載導出一個文件,請求成功時返回的是一個流形式的文件,正常導出文件。但是請求失敗的時候返回的是json ,不會處理錯誤信息,而是直接導出包含錯誤信息的文件。
可以通過返回的blob數據type類型進行區分,如果type是文件類型,導出文件,如果type是json則把blob數據轉為string,處理錯誤信息。
三、 后端設置
-
后端設置下載請求的響應頭 Content-Disposition 強制下載
這是最通用的一種方式 不受跨域和請求方式的影響Content-Disposition: attachment; filename="filename.jpg"
想使用window.open實現強制下載的可以用這種方式
在常規的 HTTP 應答中,該響應頭的值表示對響應內容的展現形式
- inline 表示將響應內容作為頁面的一部分進行展示
- attachment 表示將響應內容作為附件下載,大多數瀏覽器會呈現一個“保存為”的對話框
- filename(可選) 指定為保存框中預填的文件名