摘要:三瀏覽器方式相信大家對這個對象也不太陌生,它是標準里的一個二進制數據對象,可以與對象配合,進行文件的下載。其實這樣一個簡單的,就可以實現瀏覽器端自己的下載了。
一、背景
最近寫了一個react的組件,用來做文件導出。環境是ie10+。
細一點說,就是
1、讀取form里的數據
2、向服務端發請求,并下載文件;要求拿到請求狀態,如果出錯及時反饋給用戶。
第一個需求,我們借用了jquery的serializeArray方法,畢竟我們不想再造輪子。那接下來重點說說后面的需求。
二、一般下載文件方式大家在下載的問題的時候,一般來說,會用到
1、window.open(url);
2、window.location.href = url;
3、iframe,其實與window.open類似,但不用開啟新的tab
4、a 標簽,利用download屬性
這些方法,其實極度依賴服務端的正確性。我們可以看看服務端一旦出錯的結果。
1、window.open(url)
打開一個帶錯誤信息的頁面
2、window.location.href
頁面將跳轉到一個錯誤頁面
3、iframe
用戶感知不到任何變化
4、a標簽
直接出現 下載失敗
當然,如果response header里有content-disposition字段的話,瀏覽器都會下載一個帶錯誤信息的文件。這時候,其實我們可以多發一個ajax/fetch請求,先檢測下接口狀態,然后再取做下載邏輯。但這樣就對服務器造成了額外的開銷。
這樣的體驗都不太好,作為一個追求極致體驗的程序猿,我們應該重新思考下,如何提升用戶體驗。
三、瀏覽器FileAPI方式相信大家對blob這個對象也不太陌生,它是html5標準里的一個二進制數據對象,可以與URL 對象配合,進行文件的下載。
下面是一個最簡單的demo(我們暫時不考慮瀏覽器兼容問題)。
let blob2 = new Blob(["123"]); let url = URL.createObjectURL(blob2); let a = document.createElement("a"); a.download = "test"; a.href = url; a.click();
其實這樣一個簡單的demo,就可以實現瀏覽器端自己的下載了。那如何從服務端拿到數據,并下載呢?
這里,我們拿服務端數據,主要是通過fetch,fetch提供了一些api。其中就有一個blob的promise,我們可以把返回的數據轉成blob對象,這樣就能去下載文件了。
回到最初的需求,我們需要檢測接口的狀態。其實通過fetch,我們完全可以拿到response的信息,既然都能拿到,那控制權就在我們自手上了。
四、額外收獲-進度條按照FileApi的方式,我們是一次性從服務端拿到數據,然后再在瀏覽器端進行操作。既然是這樣,那拿數據的過程是不是就可以顯示出進度呢?這個特性,我們用以前傳統的下載方式是完全做不到的。
關于進度條,我們可以利用fetch配合reader對象來實現進度條功能,如下:
fetch(url).then(response => { var reader = response.body.getReader(); var headers = response.headers; var totalLength = headers.get("Content-Length"); var bytesReceived = 0; reader.read().then(function processResult(result) { if (result.done) { return; } bytesReceived += result.value.length; console.log(`progress: ${bytesReceived / totalLength * 100}%`); return reader.read().then(processResult); }); });
當然,有人可能會說ie下fetch會有問題。沒錯,確實會有問題,但這時候我們可以用XMLHttpRequest這個對象來實現,會更簡單直接一點。
五、常見問題 1、filename通過fileapi的方式下載文件,有個很重要的問題,就是文件名。最初的一些下載方式,都是瀏覽器自己通過判斷content-disposition這個字段來讀取文件名。那現在不一樣了,我們需要自己來讀取文件名,這時候難免要自己讀這個header,通過正則匹配下文件名。
2、cors關于cors問題,其實只要是異步請求,都會碰到。新版瀏覽器,我們常用access-control-allow-origin這個字段來解決跨域問題。這時候,我們在讀文件名的可能要留一點,記得在header里加上Access-Control-Expose-Headers這個字段,不然fetch是取不到filename信息的。具體可以看看這篇doc
3、大文件下載問題在用fileapi的時候,我們發現文件過大會讓瀏覽器崩潰,會導致文件下載失敗。目前我在測試500mb以上的文件的時候就會碰到這樣的情況。這個問題,可以通過webkitrequestfilesystem這個對象來曲線解決,但這并不是一個standard api,目前只有新版chrome支持這個對象,所以盡量不要去用。
我們推薦在拿到response的時候,讀取一下blob的size,如果發現太大,就進行降級處理,使用我們最初的那中方式。
五、總結我一直相信no silver bullet這句話,雖然fileapi這種方式能解決部分問題,但其實也有很多缺點,相信大家會在實際場景中會更深刻的感受到。所以在設計組件的時候,我們在做好優雅降級的方案同時,還特意為大家開放了各種下載方式,以適應各種場景。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79439.html
閱讀 995·2023-04-25 19:35
閱讀 2633·2021-11-22 09:34
閱讀 3679·2021-10-09 09:44
閱讀 1713·2021-09-22 15:25
閱讀 2932·2019-08-29 14:00
閱讀 3372·2019-08-29 11:01
閱讀 2596·2019-08-26 13:26
閱讀 1735·2019-08-23 18:08