這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前端涉及到的文件下載還是很多應用場景的,那麼前端文件下載有多少種方式呢?每種方式有什麼優缺點呢?下麵就來一一介紹。 1. 使用 a 標簽下載 通過a標簽的download屬性來實現文件下載,這種方式是最簡單的,也是我們比較常用的方式,先來 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
前端涉及到的文件下載還是很多應用場景的,那麼前端文件下載有多少種方式呢?每種方式有什麼優缺點呢?下麵就來一一介紹。
1. 使用 a 標簽下載
通過a
標簽的download
屬性來實現文件下載,這種方式是最簡單的,也是我們比較常用的方式,先來看示例代碼:
<a href="http://www.baidu.com" download="baidu.html">下載</a>
就上面的這個示例,我們點擊下載,發現是跳轉到了百度的首頁,並沒有真的下載文件。
因為a
標簽下載只能下載同源
的文件;如果是跨域
的文件,比如圖片、音視頻等媒體文件等都無法使用上面的a
標簽方式下載。
上面的代碼是直接通過書寫a
標簽來實現文件下載;我們也可以通過js
來實現,代碼如下:
const a = document.createElement('a') a.href = 'http://www.baidu.com' a.download = 'baidu.html' a.click()
效果和上面的一樣,都是跳轉到百度的首頁,沒有下載文件。
這裡的重點是a
標簽的download
屬性,這個屬性是HTML5
新增的。
它的作用是指定下載的文件名,如果不指定,那麼下載的文件名就會根據請求內容的Content-Disposition
來確定,如果沒有Content-Disposition
,那麼就會使用請求的URL
的最後一部分作為文件名。
2. 使用 window.open 下載
上面使用a
標簽的案例也可以通過window.open
來實現,效果是一樣的,代碼如下:
window.open('http://www.baidu.com', '_blank')
這裡的_blank
是指定用瀏覽器新視窗打開鏈接;如果不指定,那麼就會在當前頁面打開。
同樣a
標簽的download
屬性也是可以使用的,代碼如下:
window.open('http://www.baidu.com', '_blank', 'download=baidu.html')
當然這種方式也是有缺陷的,對比於a
標簽,window.open
方式不能下載.html
、.htm
、.xml
、.xhtml
等文件;因為這些文件會被當成html
文件來處理,所以會直接在當前頁面打開。
同樣也不能下載跨域
的文件,這個是window.open
實現下載原理決定的。
3. 使用 location.? 下載
以下可以實現頁面跳轉的屬性,都可以實現文件下載
3.1 location.href
// 這個方式和window.open是一樣的 location.href = 'http://www.baidu.com'
這種方式擁有window.open
的所有缺陷,所以不推薦使用,這裡只當作瞭解,所以不做過多的講解。
3.2 location.assign
location.assign('http://www.baidu.com')
3.3 location.replace
location.replace('http://www.baidu.com')
3.4 location.reload
location.reload('http://www.baidu.com')
location.reload
是有點特殊的,它的作用是重新載入當前頁面,但是它也可以接受一個參數,這個參數就是要跳轉的頁面,所以也可以實現文件下載。
當然同location.href
一樣,這些方式的缺點都一樣,同時還有屬於每個屬性自身的特性,這裡只當拓展知識,不做過多的講解。
4. XMLHttpRequest
這種方式就是我們常說的ajax
下載,包括Axios
、Fetch
等,代碼如下:
const xhr = new XMLHttpRequest() xhr.open('GET', 'http://www.baidu.com') xhr.send() xhr.onload = function () { const blob = new Blob([xhr.response], { type: 'text/html' }) const a = document.createElement('a') a.href = URL.createObjectURL(blob) a.download = 'baidu.html' a.click() }
這裡關於XMLHttpRequest
相關的知識就不做展開了,只講和文件下載相關的部分。
上面代碼主要的邏輯是當我們的請求成功後,我們會拿到響應體Response
,這個Response
就是我們要下載的內容。
然後我們把它轉換成Blob
對象,通過URL.createObjectURL
來創建一個URL
,最後使用a
標簽的download
屬性來實現文件下載。
5.1 Blob 對象
下麵是MDN
對Blob
對象的定義:
Blob
對象表示一個不可變、原始數據的類文件對象。
Blob
的數據可以按文本或二進位的格式進行讀取,也可以轉換成ReadableStream
來用於數據操作。
Blob
表示的不一定是JavaScript
原生格式的數據。
File
介面基於Blob
,繼承了Blob
的功能並將其擴展以支持用戶系統上的文件。
Blob
對象是html5
新增的對象,它的作用是用來存儲二進位數據的,比如圖片、視頻、音頻等,它的使用方法如下:
/** * @param {Array} array 二進位數據 * @param {Object} options 配置項 * @param {String} options.type 文件類型,它代表了將會被放入到 blob 中的數組內容的 MIME 類型。 * @param {String} options.endings 用於指定包含行結束符\n的字元串如何被寫入。預設為transparent,表示不會修改行結束符。還可以指定為native,表示會將\n轉換為\r\n。 */ const blob = new Blob([], { type: '' })
Tips:需要關註的是type
屬性,預設情況下, Blob
對象是沒有type
屬性的,那麼這個Blob
就是一個無類型的Blob
,文件不會損毀,但是無法被正常識別。
5.2 URL.createObjectURL
下麵是MDN
對 URL.createObjectURL
方法的定義:
URL.createObjectURL()
靜態方法會創建一個DOMString
,其中包含一個表示參數中給出的對象的URL
。
這個URL
的生命周期和創建它的視窗中的document
綁定。
這個新的URL
對象表示指定的File
對象或Blob
對象。
這個方法是用來創建一個URL
的,它的作用是把一個Blob
對象轉換成一個URL
,這個URL
可以用來下載文件,也可以用來預覽文件,代碼如下:
const url = URL.createObjectURL(blob)
這裡需要註意的是,這個URL
的生命周期和創建它的視窗中的document
綁定。
也就是說,當我們的document
被銷毀後,這個URL
就會失效,所以我們需要在合適的時機銷毀它。
代碼如下:
URL.revokeObjectURL(url)
回到我們剛纔下載的問題,我們是通過Blob
對象來解決,但是我們的type
屬性是寫死的,如果在文件類型是確定的情況下是沒問題的。
但是如果這個介面就是下載文件的介面,文件可能是各種類型的,我們應該怎麼處理?
這裡的沒有正確答案,第一個可以和介面提供者進行協商,協商方案是不確定的;第二就是通過Response
的header
來獲取文件的type
,也是我們要講的:
const type = response.headers['content-type'] const blob = new Blob([response.data], { type })
這裡我們通過Response
的header
來獲取type
,然後再創建Blob
對象,這樣就可以正確的下載文件了。
其實content-type
也可能是application/octet-stream
,這個時候我們就需要通過file-type
來獲取文件的type
了。
下麵的代碼是通過file-type
來獲取文件的type
:
import {fileTypeFromStream} from 'file-type'; const type = await fileTypeFromStream(response.body); const blob = new Blob([response.data], { type })
5. 總結
上面的方案這麼多,其實最終還是落到a
標簽上,所以不管是通過瀏覽器的內置行為進行下載,還是通過ajax
進行下載,文件下載的最終還是瀏覽器的行為。