前端文件下載+js監聽文件下載完成功能

值得注意

  • 下載文件,清除緩存策略(最好后端接口處理,前端處理有些瀏覽器不識別)
    后端: 接口再頭部添加清除緩存標記
    前端: 請求連接后面添加時間戳或者隨機數

  • 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(可選) 指定為保存框中預填的文件名
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網絡請求組件 FMDB本地數據庫組件 SD...
    陽明AGI閱讀 16,003評論 3 119
  • 又是一次旅行,在旅行中隨手的用用APP提升一下旅行的品質十分必要。雖然說我用的也不是什么特別小眾的應用,在這里記錄...
    ouzar閱讀 374評論 0 4
  • 新買的不粘煎鍋到了。(在大姐家看到了麥飯石不粘鍋,于是就買了回來。) 很期待明天的到來,用它來做早餐。 做什么呢,...
    雜亂有章閱讀 153評論 0 0
  • 今天晚上我到哥哥家玩123木頭人。玩的可開心啦!吃飯的時候,媽媽過來叫我們吃飯。蒜黃,肉,白菜,大餅,饅頭,我吃了...
    平凡一生123閱讀 5,479評論 0 1