在後臺管理系統中,咱們常常會遇到文件導出這個需求,下面,我將幾種常見的導出方式作一個簡單的介紹,讓你們在之後遇到此類需求時,可以切合實際狀況,採起相對合理的方式。html
文件地址
已經存在服務器上的文件,好比用戶上傳的圖片、材料等等http://192.168.1.103:3000/imgs/bg.jpg
前端
導出接口
根據用戶需求,動態生成的文件,常見的好比導出業務流水錶格,數據彙總表格等等http://192.168.1.103:3000/api/export
html5
html5新增的屬性git
文件名由前端指定,前臺下達保存指令github
缺點:ie不支持,而且,在跨域時,即便後臺設置了容許跨域的響應頭,也沒法下載,也就是說,必須與當前域一致ajax
<a href="http://192.168.1.103:3000/imgs/xx.jpg" download /> <a href="http://192.168.1.103:3000/imgs/xx.jpg" download="xx.jpg" />
文件名由前臺指定,前臺下達保存指令chrome
將後臺返回的二進制數據,轉換成blob,而後利用URL.createObjectURL,建立一個指向內存中blob的URL,再使用a標籤的download屬性進行導出api
缺點:ie不支持,blob有內存限制跨域
可是避免了單獨使用a.download必須與域名一致的問題瀏覽器
function useLinkDownload(url, fileName) { const link = document.createElement("a"); link.style.display = "none"; link.href = url; link.download = fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); } $.get('http://192.168.1.103:3000/api/export', { responseType: 'blob' }) .then(function(res) { // 建立一個指向內存中blob的URL const objectURL = URL.createObjectURL(res.data); useLinkDownload(objectURL, 'xx.xlsx') URL.revokeObjectURL(objectURL) })
文件名由前臺指定,前臺下達保存指令
ie專有api
缺點:chrome、firefox不支持,blob有內存限制
$.get('http://192.168.1.103:3000/api/export', { responseType: 'blob' }) .then(function(res) { navigator.msSaveBlob(res.data, "xx.xlsx"); })
文件名由後臺指定,此種方式指定的文件名優先級高於a.download
前端發送請求便可,須要注意的一點是,不能與ajax組合(使用ajax後,會變成二進制流,須要前端對流處理)
var url = 'http://192.168.1.103:3000/api/export'
function useLink(url) { const link = document.createElement("a"); link.style.display = "none"; link.href = url; document.body.appendChild(link); link.click(); document.body.removeChild(link); } useLink(url)
function useLocationDownload(url) { window.location.href = url; } useLocationDownload(url)
function useWindowOpenDownload(url) { window.open(url); } useWindowOpenDownload(url)
function useFormDownload(url) { const form = document.createElement("form"); form.action = url; form.method = "get"; form.style.display = "none"; document.body.appendChild(form); form.submit(); form.remove(); } useFormDownload(url)
function useIframeDownload(url) { const iframe = document.createElement("iframe"); iframe.src = url; iframe.style.display = "none"; document.body.appendChild(iframe); document.body.removeChild(iframe); } useIframeDownload(url)
一些問題
跨域
a.download無效,即便後臺設置了Access-Control-Allow-Origin也無效,download的值須要與當前域名一致,讓ngnix進行轉發能夠解決。
https://html.spec.whatwg.org/dev/links.html#downloading-resources
大文件
ajax的方式須要用到blob,而blob是有限制的,例如chrome的上限是2GB,因此ajax的方式須要被排除,可選的方式是a.download和Content-Disposition,這兩種方式沒有用到blob,因此也沒有具體的限制。
在使用a標籤或者form時,爲了不在導出時發生頁面閃爍現象,咱們可使用target非_self的值,來避免,可是當target爲_self時,若是導出請求失敗了,頁面會被覆蓋掉,用哪一種方式能夠避免這個問題?
可選的方式爲ajax和iframe,這二者均可以免失敗時覆蓋掉當前頁面,而且ajax還能夠告訴用戶失敗的緣由
總結
默認狀況下,瀏覽器面對自身沒法打開的文件,都會採起將其保存到本地方式,可是如圖片,文本文件以及 pdf,瀏覽器首先嚐試打開,這在通常狀況下是合理的,但當咱們的目的是保存而不是打開時,這就會變成一個問題。
上文羅列了幾種導出方式以及各自的侷限性,綜合來看 Content-Disposition 應該是比較通用的方式,即兼顧了兼容性,也避免了瀏覽器內存限制。
https://github.com/eligrey/FileSaver.js/wiki/Saving-a-remote-file#using-http-header