前端檔案下載通識篇

RobinsonZhang發表於2018-10-28

前言

前端如何實現下載檔案呢?隨著前端技術的發展,越來越多的前端需求中會出現下載檔案這樣的需求。

看著掘金很多人在近期不斷的分享有關的文章,我總結了下自己的經驗,根據不同情況,總結了一篇算是前端檔案下載的通識篇,如果你對這方面完全不懂或者沒有任何方案,那麼本文會給你一個很不錯的啟示。

方案一 :原生提交,後端返回檔案流

這種方式是利用form.submit直接向後端提交,後端返回檔案流生成的檔案,後端處理成功後會直接返回到頁面,瀏覽器會整理並開啟自己的儲存下載檔案機制 。

優點 :沒有相容問題,傳統方式

缺點:拿不到後端處理這個過程的時機,無法根據回撥函式做互動以及進度提示

// 後端參考程式碼
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", HttpUtility.UrlEncode("諮詢記錄匯出.xls", Encoding.GetEncoding("UTF-8")));
// 參考程式碼
function exportRecord() {
   var $form = $("<form>"); //定義一個form表單
    $form.hide().attr({target:'',method:'post','action':'/xxx'});
    var $input = $("<input>");
    $input.attr({"type":"hidden","name":'req'}).val(req);
    $form.append($input).appendTo($("body")).submit().remove();
 }
複製程式碼

方案 二 :ajax提交,後端返回線上檔案地址

利用ajax或者新生的axios去提交請求,後端會返回一個線上的檔案地址,前端可以通過原生的window.open開啟這個地址就可以實現下載;也可以通過a標籤設定href以及download屬性,並自動點選實現其下載功能,關於其相容性問題,可以判斷download屬性是否存在來彌補。

優點 :可以拿到其返回時機,可以做互動

缺點 :線上會儲存大量的中間臨時檔案,可以用設定時限來優化。另外涉及使用者隱私的問題,可以用token等驗證機制實現。

// 參考方案
$.ajax({
                type: "post",
                url: "/xxx",
                data: data,
                success: function (res) {
                    tool.loadingend();
                    if(res.Status){
                    // window.open或者a標籤下載 
                    var isSupportDownload = 'download' in document.createElement('a');
                    if(isSupportDownload){
                    var $a = $("<a>") ;
                    $a.attr({href:res.url,download:'filename'}).hide().appendTo($("body"))[0].click();
                    }else{
                       window.open(res.url)
                    }
                    }else{
                      tool.tip(res.Message);
                    }
                }
            })
複製程式碼
  • a標籤download屬性
  • 方案二 :補充方案 :利用form表單提交下載檔案(ajax無法直接處理返回的檔案型別),用於解決window.open方案被瀏覽器攔截的情況。
let $form = $("<form>") ;
$form.attr({method:"get",action:res.Message}).hide();
let queryStr = res.Message.split("?")[1];
let queryObj = qs.parse(queryStr) ;
$("body").append($form);
for(let p in queryObj){
    let $input =$("<input type='hidden'>") ;
    $input.attr({"name":p,value:queryObj[p]}).appendTo($form);
}
$form.submit().remove(); 
複製程式碼

方案三 :前端利用download模組進行下載

支援場景 : 與上面的方案相比,這個模組提供的方案更加完善,而不是侷限於某種方案,使用率很高。在原始碼中,我們可以看到在這個模組中針對各個瀏覽器和相應的屬性是否支援進行了比較全面的相容。其對應的下載檔案方案包括了以下幾種。

  • window.open(url)開啟某個檔案地址
  • iframe的框架中,設定src屬性,通過iframe進行檔案的下載,支援檔案地址
  • 通過form標籤,設定action的檔案地址,然後通過form的提交來完成檔案的下載(支援二進位制)

方案小結: 對於常規的支援檔案地址的下載,相容性非常好,而對於傳統的檔案流性質的,通過form標籤也可以進行簡單的支援,可以說是非常好的方案了。當然如果你需要那麼全面的方案,大多數情況用其中一個就可以了。

方案四 :h5新生方案下載

這個我覺得張鑫旭大佬介紹的蠻多的,應該上手足夠了,就不多介紹了。除了a標籤提供的download屬性,多介紹了一種html:blob的方式。另外針對圖片可以通過base64的方式。

傳送門:h5新方式下載檔案

個人建議:雖然新技術很好,但酌情使用,而且這裡沒有考慮任何相容,也沒有談論到其他的一些檔案型別,比如表格,pdf,大檔案,視訊音訊的下載情況等。所以不是很建議把這個當做很常規的方案來考慮。

方案五 :file-saver

模組地址npm.taobao.org/package/fil… github託管地址:github.com/eligrey/Fil…

在模組的介紹中:詳細說明了瀏覽器支援的情況,以及可以支援的下載範圍,儲存為的檔案型別,與其我們去用基礎知識踩雷,還是建議大家用成熟的模組方案去解決需求相關的問題。支援不了就退步用傳統的方案解決,讓後端提供直接的檔案地址,要知道後端有更多的成熟的技術架包,對於前端來說還是萌新不確定的方案,後端早已經有了答案。

說明:我們之前的需求是希望下載一個表格檔案,之前的方案是用後端生成檔案地址,然後進行下載,其設定的返回response content type 為application/vnd.ms-excel (常規型別application/json)。後面發現有這個模組,基本使用還是體驗蠻好的,此時的約定變成了後端根據查詢的資料生成一個二進位制的檔案流,這樣的好處是如果麼有必要的時候可以減少在阿里雲或者其他伺服器暫存很多檔案。

擴充思考下:在大家的公司裡有沒有遇到過類似的需求,按照我之前的經驗是本來是想後端返回一個生成之後的檔案地址,但後端的回覆是由於採用了負載均衡,這個地址再去請求時不一定會請求到這個伺服器,所以之前的前後端協調方案是放到了阿里雲,然後通過設定許可權和時效來保證檔案的臨時性,使用者也可以在相似請求時不用重複請求資料庫,重新生成檔案,因為重複的資料內容會直接返回已經上傳到阿里雲的檔案地址。

原始碼解析: 在其原始碼中,主要是針對返回的http的resonsetype做了要求,然後針對返回的地址進行處理,其中涉及到重要的程式碼:

//利用a標籤下載
var a = document.createElement('a')
a.href = blob
//觸發點選事件
node.dispatchEvent(new MouseEvent('click'))
// reader 進行解析
var reader = new FileReader()
var url = reader.result
//得到可解析的地址
_global.URL || _global.webkitURL,
URL.createObjectURL(blob)
//對 cors 跨域是否支援
  return xhr.status >= 200 && xhr.status <= 299
複製程式碼

filereader的官方介紹:developer.mozilla.org/zh-CN/docs/…

總結

綜上,無論是偏傳統的方案,還是比較全面相容的根據檔案地址下載的方式,還是h5新出的webapi的方式都有比較好的認識,如果你對相關的知識點或者方案有進一步研究的興趣,建議針對官方api的相關文章或者已經開源出的兩個模組進行深度的優化和研究效率更佳。

覺得還不錯,給個贊加關注吧,謝謝大家的支援。

相關文章