前言
前端如何實現下載文件呢?隨著前端技術的發(fā)展,越來越多的前端需求中會出現下載文件這樣的需求。
看著掘金很多人在近期不斷的分享有關的文章,我總結了下自己的經驗,根據不同情況,總結了一篇算是前端文件下載的通識篇,如果你對這方面完全不懂或者沒有任何方案,那么本文會給你一個很不錯的啟示。
方案一 :原生提交,后端返回文件流
這種方式是利用form.submit直接向后端提交,后端返回文件流生成的文件,后端處理成功后會直接返回到頁面,瀏覽器會整理并打開自己的保存下載文件機制 。
優(yōu)點 :沒有兼容問題,傳統方式
缺點:拿不到后端處理這個過程的時機,無法根據回調函數做交互以及進度提示
// 后端參考代碼
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屬性是否存在來彌補。
優(yōu)點 :可以拿到其返回時機,可以做交互
缺點 :線上會存儲大量的中間臨時文件,可以用設置時限來優(yōu)化。另外涉及用戶隱私的問題,可以用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的提交來完成文件的下載(支持二進制)
方案小結:
對于常規(guī)的支持文件地址的下載,兼容性非常好,而對于傳統的文件流性質的,通過form標簽也可以進行簡單的支持,可以說是非常好的方案了。當然如果你需要那么全面的方案,大多數情況用其中一個就可以了。
方案四 :h5新生方案下載
這個我覺得張鑫旭大佬介紹的蠻多的,應該上手足夠了,就不多介紹了。除了a標簽提供的download屬性,多介紹了一種html:blob的方式。另外針對圖片可以通過base64的方式。
傳送門:h5新方式下載文件
個人建議:雖然新技術很好,但酌情使用,而且這里沒有考慮任何兼容,也沒有談論到其他的一些文件類型,比如表格,pdf,大文件,視頻音頻的下載情況等。所以不是很建議把這個當做很常規(guī)的方案來考慮。
方案五 :file-saver
模塊地址:https://npm.taobao.org/package/file-saver,推薦使用,下載量以及穩(wěn)定性足夠好。支持新出的h5特性的加載方式,也就是方案五的部分。
github托管地址:https://github.com/eligrey/FileSaver.js
在模塊的介紹中:詳細說明了瀏覽器支持的情況,以及可以支持的下載范圍,保存為的文件類型,與其我們去用基礎知識踩雷,還是建議大家用成熟的模塊方案去解決需求相關的問題。支持不了就退步用傳統的方案解決,讓后端提供直接的文件地址,要知道后端有更多的成熟的技術架包,對于前端來說還是萌新不確定的方案,后端早已經有了答案。
說明:我們之前的需求是希望下載一個表格文件,之前的方案是用后端生成文件地址,然后進行下載,其設置的返回response content type 為application/vnd.ms-excel (常規(guī)類型application/json)。后面發(fā)現有這個模塊,基本使用還是體驗蠻好的,此時的約定變成了后端根據查詢的數據生成一個二進制的文件流,這樣的好處是如果么有必要的時候可以減少在阿里云或者其他服務器暫存很多文件。
拓展思考下:在大家的公司里有沒有遇到過類似的需求,按照我之前的經驗是本來是想后端返回一個生成之后的文件地址,但后端的回復是由于采用了負載均衡,這個地址再去請求時不一定會請求到這個服務器,所以之前的前后端協調方案是放到了阿里云,然后通過設置權限和時效來保證文件的臨時性,用戶也可以在相似請求時不用重復請求數據庫,重新生成文件,因為重復的數據內容會直接返回已經上傳到阿里云的文件地址。
源碼解析:
在其源碼中,主要是針對返回的http的resonsetype做了要求,然后針對返回的地址進行處理,其中涉及到重要的代碼:
//利用a標簽下載
var a = document.createElement('a')
a.href = blob
//觸發(fā)點擊事件
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的官方介紹:https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
總結
綜上,無論是偏傳統的方案,還是比較全面兼容的根據文件地址下載的方式,還是h5新出的webapi的方式都有比較好的認識,如果你對相關的知識點或者方案有進一步研究的興趣,建議針對官方api的相關文章或者已經開源出的兩個模塊進行深度的優(yōu)化和研究效率更佳。
覺得還不錯,給個贊加關注吧,謝謝大家的支持。