1. 程式人生 > >純前端下載pdf連結檔案,而不是開啟預覽的解決方案

純前端下載pdf連結檔案,而不是開啟預覽的解決方案

純前端下載pdf連結檔案,而不是開啟預覽的解決方案

一,介紹與需求

 1.1,介紹

      XMLHttpRequest 用於在後臺與伺服器交換資料。這意味著可以在不重新載入整個網頁的情況下,對網頁的某部分進行更新。

      問題:Chrome 會自動呼叫內建的 pdf 閱讀器開啟

 1.2,需求

      在谷歌(Chrome)瀏覽器中,使用a標籤屬性download下載pdf連結檔案,如果是相同域時,可以直接下載;但是如果域不同,則不是下載,而是直接開啟頁面預覽檔案。但是需求是直接點選下載檔案,而不是開啟預覽;以及下載後臺返回的檔案流。

二,下載檔案

2.1,思路

    通過a標籤的download屬性,我們可以直接下載後臺介面返回的資料流檔案;故此,我們是否可以模擬傳送http請求,將檔案連結轉換成檔案流來使用a標籤download下載。以下主要介紹連結檔案轉檔案流下載的思路與方法

2.2,檔案路徑轉檔案流

1,先校驗是否是路徑連結

使用正則表示式校驗url是否合法

1 let reg = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+).)+([A-Za-z0-9-~\/])+$/;
2  if (!reg.test(url)) {
3     throw new Error("傳入引數不合法,不是標準的連結");
4  }

2,建立XMLHttpRequest物件

模擬傳送http請求,獲取檔案流

 1  let xhr = new XMLHttpRequest();//建立 XMLHttpRequest 物件
 2  xhr.open('get', 'http://url', true);//規定請求的型別、URL 以及是否非同步處理請求。三個引數分別是 method:請求的型別;GET 或 POST url:檔案在伺服器上的位置 async:true(非同步)或 false(同步)
 3  xhr.setRequestHeader('Content-Type', `application/pdf`);//設定請求頭
 4  xhr.responseType = "blob";//返回的資料型別 這兒需要blob物件
 5  xhr.onload = function () {//請求成功回撥函式
 6       if (this.status == 200) {
 7         //接受二進位制檔案流
 8         var blob = this.response;
 9       }
10     }
11  xhr.send();//將請求傳送到伺服器

3,完整方法

 1 /**
 2  * 檔案連結轉檔案流下載--主要針對pdf 解決谷歌瀏覽器a標籤下載pdf直接開啟的問題
 3  * @param url  :檔案連結
 4  * @param fileName  :檔名;
 5  * @param type  :檔案型別;
 6  */
 7 function fileLinkToStreamDownload(url, fileName, type) {
 8   let reg = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+).)+([A-Za-z0-9-~\/])+$/;
 9   if (!reg.test(url)) {
10     throw new Error("傳入引數不合法,不是標準的檔案連結");
11   } else {
12     let xhr = new XMLHttpRequest();
13     xhr.open('get', url, true);
14     xhr.setRequestHeader('Content-Type', `application/${type}`);
15     xhr.responseType = "blob";
16     xhr.onload = function () {
17       if (this.status == 200) {
18         //接受二進位制檔案流
19         var blob = this.response;
20         downloadExportFile(blob, fileName, type)
21       }
22     }
23     xhr.send();
24   }
25 }

 2.3,下載檔案

1,建立下載連結

1 let downloadElement = document.createElement('a');
2 let href = blob;
3   if (typeof blob == 'string') {
4     downloadElement.target = '_blank';//如果是連結,開啟新標籤頁下載
5   } else {
6     href = window.URL.createObjectURL(blob); //建立下載的連結
7   }
8  downloadElement.href = href;//下載連結

2,模擬點選下載連結

1 downloadElement.download = tagFileName + moment(new Date().getTime()).format('YYYYMMDDhhmmss') + '.' + fileType; //下載後文件名
2 document.body.appendChild(downloadElement);
3 downloadElement.click(); //點選下載

3,下載完成後釋放資源

1   document.body.removeChild(downloadElement); //下載完成移除元素
2   if (typeof blob != 'string') {
3     window.URL.revokeObjectURL(href); //釋放掉blob物件
4   }

4,完成方法

 1 /**
 2  *下載匯出檔案
 3  * @param blob  :返回資料的blob物件或連結
 4  * @param tagFileName  :下載後文件名標記
 5  * @param fileType  :檔案類 word(docx) excel(xlsx) ppt等
 6  */
 7 function downloadExportFile(blob, tagFileName, fileType) {
 8   let downloadElement = document.createElement('a');
 9   let href = blob;
10   if (typeof blob == 'string') {
11     downloadElement.target = '_blank';
12   } else {
13     href = window.URL.createObjectURL(blob); //建立下載的連結
14   }
15   downloadElement.href = href;
16   downloadElement.download = tagFileName + moment(new Date().getTime()).format('YYYYMMDDhhmmss') + '.' + fileType; //下載後文件名
17   document.body.appendChild(downloadElement);
18   downloadElement.click(); //點選下載
19   document.body.removeChild(downloadElement); //下載完成移除元素
20   if (typeof blob != 'string') {
21     window.URL.revokeObjectURL(href); //釋放掉blob物件
22   }
23 
24 }

 2.4,base64物件轉檔案物件

主要針對圖片,不過其他檔案也可

 1 /**
 2  * base64物件轉檔案物件
 3  * @param urlData  :資料的base64物件
 4  * @param type  :型別 image/png;
 5  * @returns {Blob}:Blob檔案物件
 6  */
 7 function base64ToBlob(urlData, type) {
 8   let arr = urlData.split(',');
 9   let array = arr[0].match(/:(.*?);/)
10   let mime = (array && array.length > 1 ? array[1] : type) || type;
11   // 去掉url的頭,並轉化為byte
12   let bytes = window.atob(arr[1]);
13   // 處理異常,將ascii碼小於0的轉換為大於0
14   let ab = new ArrayBuffer(bytes.length);
15   // 生成檢視(直接針對記憶體):8位無符號整數,長度1個位元組
16   let ia = new Uint8Array(ab);
17   for (let i = 0; i < bytes.length; i++) {
18     ia[i] = bytes.charCodeAt(i);
19   }
20   return new Blob([ab], {
21     type: mime
22   });
23 }

 2.5,使用例項

1,檔案連結轉檔案流下載

1 fileLinkToStreamDownload('http://127.0.0.1/download.pdf', '下載檔案例項', 'pdf')

2,base64物件轉檔案物件下載

1 let blob = base64ToBlob('data:image/png;base64,iVBORw0KGgo=...','image/png')//獲取圖片的檔案流
2 downloadExportFile(blob, 'download', 'png')

  問題記錄:瀏覽器快取問題

  由於瀏覽器的快取機制,當我們使用XMLHttpRequest發出請求的時候,瀏覽器會將請求的地址與快取中的地址進行比較,如果存在相同記錄則根據不向伺服器發出請求而直接返回與上一次請求相同內容。

  解決這類快取問題的辦法:

1,時間戳方法 —即在每次請求的url後面加上當前時間的字串或其他類似的不會重複的隨機字串,這樣瀏覽器每次發出的是不同的url,即會當做不同的請求來處理,而不會從快取中讀取。

1  if(url.indexOf("?")>=0){//判斷url中是否已經帶有引數
2         url = url + "&t=" + (new Date()).valueOf();
3   }else{
4        url = url + "?t=" + (new Date()).valueOf();
5       }

2,在XMLHttpRequest傳送請求之前加上:

加If-Modified-Since頭

1 xhr.setRequestHeader("If-Modified-Since","0");  
2 xhr.send(null);

&n