網頁中直接下載 PDF 檔案而不開啟新的頁面載入 PDF 檔案
我們知道 <a>
元素有 download
屬性,表示當前連結不是用來瀏覽的,而是用來下載的。它的值是一個字串,表示使用者下載得到的檔名。可是對於 PDF
檔案,瀏覽器預設開啟一個新的頁面載入 PDF
檔案,而不會直接下載該檔案。
這時候我們需要將原來的用於下載的 url 進行轉換,轉換成一個 Blob
物件的 URL
。
一、用 Blob
物件來讀寫 PDF
檔案
引入 Blob
物件,用 Blob
(Binary Large Object
二進位制大型檔案) 物件來讀寫 PDF
檔案。
瀏覽器原生提供 Blob()
建構函式,用來生成例項物件。
new Blob(array [, options])
Blob
建構函式接受兩個引數。第一個引數是陣列,成員是字串或二進位制物件,表示新生成的 Blob
例項物件的內容;第二個引數是可選的,是一個配置物件,目前只有一個屬性 type
,它的值是一個字串,表示資料的 MIME
型別,預設是空字串。
let htmlFragment = ['<a id="a"><b id="b">hey!</b></a>'];
let myBlob = new Blob(htmlFragment, { type: 'text/html' });
上面程式碼中,例項物件 myBlob
包含的是字串。生成例項的時候,資料型別指定為 text/html
下面是另一個例子,Blob
儲存 JSON
資料。
let obj = { hello: 'world' };
let blob = new Blob([JSON.stringify(obj)], { type: 'application/json' });
blob; // Blob{size: 17, type: "application/json"}
對於 MIME
型別,需要選擇適合二進位制檔案流的型別,而不是普通的文字型別:(MIME
型別)
瀏覽器通常使用
MIME
型別(而不是副檔名)來確定如何處理URL
,因此Web
伺服器在響應頭中新增正確的MIME
型別非常重要。如果配置不正確,瀏覽器可能會曲解檔案內容,網站將無法正常工作,並且下載的檔案也會被錯誤處理。
還有一種很重要的 MIME
型別 application/octet-stream
這是應用程式檔案的預設值。意思是 未知的應用程式檔案 ,瀏覽器一般不會自動執行或詢問執行。瀏覽器會像對待 設定了
HTTP
頭Content-Disposition
值為attachment
的檔案一樣來對待這類檔案。
二、通過 URL.createObjectURL()
方法將 Blob 物件轉換成 url
URL.createObjectURL()
方法將流媒體檔案生成一個 URL
字串。這個字串代表了 Blob
物件的 URL
。
該方法生成的 URL
就像下面的樣子(以 blob:
開頭的字串)。
blob:http://localhost:3209/28c3a781-3492-4cff-82cc-2f72a0a7f245
至此我們就拿到了 Blob
物件的 URL
。
當我們拿到下載 PDF
檔案的地址(url
)和 PDF
檔名(name
)後,先轉換成二進位制檔案流的 url
,然後就可以下載該檔案了。
handlePdfLink(url: string, name: string): void {
fetch(url, {
method: 'get'
})
.then(function (res) {
if (res.status !== 200) {
return res.json()
}
return res.arrayBuffer()
})
.then((blobRes) => {
// 生成 Blob 物件,設定 type 等資訊
const e = new Blob([blobRes], {
type: 'application/octet-stream'
})
// 將 Blob 物件轉為 url
this.blobLink = window.URL.createObjectURL(e)
this.downloadFile(this.blobLink, name)
}).catch(err => {
console.error(err)
})
}
上面程式碼中 arrayBuffer()
將產生一段二進位制資料。Response.arrayBuffer()
arrayBuffer()
接受一個Response
流, 並等待其讀取完成. 它返回一個promise
例項, 並resolve
一個ArrayBuffer
物件。
然後將檔案的MIME
型別設定為 application/octet-stream
讓瀏覽器不會自動執行或詢問執行,裝入 Blob
物件中進行讀取檔案的資料內容。
再使用 URL.createObjectURL()
方法,針對 Blob
物件生成一個臨時 URL
,以便於某些 API
使用。這個 URL
以 blob:
開頭,表明對應一個 Blob
物件,協議頭後面是一個識別符,用來唯一對應記憶體裡面的 Blob
物件。這一點與 data://URL
(URL
包含實際資料)和 file://URL
(本地檔案系統裡面的檔案)都不一樣。
最後我們新增 <a>
元素給它裝入下載地址 url
和下載檔名 name
來下載 PDF
檔案。
downloadFile(url: string, name: string): void {
if (url && url.trim()) {
let a = document.createElement('a');
a.href = url;
a.download = name || '未命名檔案';
a.click();
window.URL.revokeObjectURL(this.blobLink);
} else {
this.msg.remove();
this.msg.error('檔案下載路徑不能為空!');
}
}
由於每次使用 URL.createObjectURL()
方法,都會在記憶體裡面生成一個 URL
例項。如果不再需要該方法生成的 URL
字串,為了節省記憶體,下載完成後可以使用 URL.revokeObjectURL()
方法釋放這個例項。