純前端下載pdf鏈接文件,而不是打開預覽的解決方案
一,介紹與需求
1.1,介紹
XMLHttpRequest 用於在后台與服務器交換數據。這意味着可以在不重新加載整個網頁的情況下,對網頁的某部分進行更新。
問題:Chrome 會自動調用內置的 pdf 閱讀器打開
1.2,需求
在谷歌(Chrome)瀏覽器中,使用a標簽屬性download下載pdf鏈接文件,如果是相同域時,可以直接下載;但是如果域不同,則不是下載,而是直接打開頁面預覽文件。但是需求是直接點擊下載文件,而不是打開預覽;以及下載后台返回的文件流。
二,下載文件
已發布npm包:web-downloadfile,運行如下命令即可安裝使用
cnpm install web-downloadfile --save
目前只提供三個Api,分別如下:
import { base64ToFileOrBlob, saveFileToBlob, saveFileToLink } from 'web-downloadfile';
詳細的使用方式可查看官網web-downloadfile
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);