前端實現檔案下載功能
1、通過window.open()開啟新頁面下載檔案
window.open(`url`,'_self')
使用場景:下載excel檔案,後端提供介面,介面返回的是檔案流,可以直接使用window.open(),最簡單的方式。
優點:最簡潔;
弊端:當引數錯誤時,或其它原因導致介面請求失敗,這時無法監聽到介面返回的錯誤資訊,需要保證請求必須是正確的且能正確返回資料流,不然開啟頁面會直接輸出介面返回的錯誤資訊,體驗不好。
2、通過a標籤開啟新頁面下載檔案
export const exportFile = (url, fileName) => {
const link = document.createElement('a')
const body = document.querySelector('body')
link.href = url
link.download = fileName
// fix Firefox
link.style.display = 'none'
body.appendChild(link)
link.click()
body.removeChild(link)
}
通過a標籤下載的方式,同window.open()是一樣的,唯一的優點是可以自定義下載後的檔名,a標籤裡有download屬性可以自定義檔名。
弊端:同window.open()方式一樣,無法監聽錯誤資訊。
問題:以上兩種方式,當在下載.mp3格式,或者視訊檔案時,瀏覽器會直接播放該檔案,而達不到直接下載的功能,此時,當下載音視訊檔案時無法使用以上兩種方式。
3、通過檔案流的方式下載
為了解決.mp3檔案下載所帶來的問題,通過ajax請求返回Blob物件,或者ArrayBuffer物件。
(1)、獲取檔案
如下:通過原生ajax請求返回Blob物件
const getBlob = (url) => {
return new Promise((resolve) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.responseType = 'blob'
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response)
}
}
xhr.send()
})
}
同樣,也可以通過axios返回ArrayBuffer物件,同等作用
import axios from 'axios'
const getFile = url => {
return new Promise((resolve, reject) => {
axios({
method:'get',
url,
responseType: 'arraybuffer'
}).then(data => {
resolve(data.data)
}).catch(error => {
reject(error.toString())
})
})
}
ArrayBuffer(又稱型別化陣列)
ArrayBuffer物件用來表示通用的、固定長度的原始二進位制資料緩衝區。ArrayBuffer 不能直接操作,而是要通過型別陣列物件或 DataView 物件來操作,它們會將緩衝區中的資料表示為特定的格式,並通過這些格式來讀寫緩衝區的內容。
Blob(Binary Large Object): 二進位制大資料物件
Blob 物件表示一個不可變、原始資料的類檔案物件。Blob 表示的不一定是JavaScript原生格式的資料。File 介面基於Blob,繼承了 blob 的功能並將其擴充套件使其支援使用者系統上的檔案。
注意:
如果下載檔案是文字型別的(如: .txt, .js之類的), 那麼用responseType: 'text'也可以, 但是如果下載的檔案是圖片, 視訊之類的, 就得用arraybuffer或blob,更多詳情請檢視MDN
通過ajax請求的方式下載檔案,可以解決第1、2中存在的弊端,當請求錯誤時或捕獲到錯誤資訊
(2)、儲存檔案
當獲取到檔案後,這時需要儲存檔案
const saveAs = (blob, filename) => {
if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename)
} else {
const link = document.createElement('a')
const body = document.querySelector('body')
link.href = window.URL.createObjectURL(blob) // 建立物件url
link.download = filename
// fix Firefox
link.style.display = 'none'
body.appendChild(link)
link.click()
body.removeChild(link)
window.URL.revokeObjectURL(link.href) // 通過呼叫 URL.createObjectURL() 建立的 URL 物件
}
}
為了解決IE(ie10 - 11)和Edge無法開啟Blob URL連結的方法,微軟自己有一套方法window.navigator.msSaveOrOpenBlob(blob, filename),開啟並儲存檔案,以上程式碼做了簡單的相容,navigator.msSaveBlob(blob, filename)是直接儲存。注意,此為非標準功能,詳情請檢視相關文件。
以下為完整程式碼
const getBlob = (url) => {
return new Promise((resolve) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.responseType = 'blob'
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response)
}
}
xhr.send()
})
}
const saveAs = (blob, filename) => {
if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename)
} else {
const link = document.createElement('a')
const body = document.querySelector('body')
link.href = window.URL.createObjectURL(blob) // 建立物件url
link.download = filename
// fix Firefox
link.style.display = 'none'
body.appendChild(link)
link.click()
body.removeChild(link)
window.URL.revokeObjectURL(link.href) // 通過呼叫 URL.createObjectURL() 建立的 URL 物件
}
}
export const download = (url, filename = '') => {
getBlob(url).then((blob) => {
saveAs(blob, filename)
})
}
資源搜尋網站大全 https://www.renrenfan.com.cn 廣州VI設計公司https://www.houdianzi.com
4、如何實現批量下載,且打包檔案
在第3點的基礎上,如果要實現批量下載,那能做到的只是連續多次呼叫download方法,這樣無法批量集中的下載檔案。這個時候就需要能夠對已獲取到的檔案流,進行一個打包的操作,然後一次下載完畢。
這時,需要用到兩個庫jszip和file-saver
完整的思路,通過ajax獲取檔案,然後用jszip壓縮檔案, 再用file-saver生成檔案
(1)、獲取檔案
同第3點中的第(1)點
(2)、打包檔案
export const download = () => {
const urls = ['url', 'url'] //需要下載的路徑
const zip = new JSZip()
const cache = {}
const promises = []
urls.forEach((item) => {
const promise = getBlob(item).then((data) => { // 下載檔案, 並存成ArrayBuffer物件
zip.file('下載檔名', data, { binary: true }) // 逐個新增檔案
cache[item.fileName] = data
})
promises.push(promise)
})
Promise.all(promises).then(() => {
zip.generateAsync({ type: 'blob' }).then((content) => { // 生成二進位制流
FileSaver.saveAs(content, `打包下載.zip`) // 利用file-saver儲存檔案
})
})
}
相關jszip/file-saver更多詳情
jszip:
https://github.com/Stuk/jszip
http://stuk.github.io/jszip/
file-saver:
https://github.com/eligrey/
貼上完整程式碼
/**
* 獲取檔案
* @param url
* @returns {Promise<any>}
*/
const getBlob = (url) => {
return new Promise((resolve) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.responseType = 'blob'
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response)
}
}
xhr.send()
})
}
/**
* 批量打包zip包下載
* @param urlArr Array [{url: 下載檔案的路徑, fileName: 下載檔名稱}]
* @param filename zip檔名
*/
export const download = (urlArr, filename = '打包下載') => {
if (!urlArr.length > 0) return
const zip = new JSZip()
const cache = {}
const promises = []
urlArr.forEach((item) => {
const promise = getBlob(item.url).then((data) => { // 下載檔案, 並存成ArrayBuffer物件
zip.file(item.fileName, data, { binary: true }) // 逐個新增檔案
cache[item.fileName] = data
})
promises.push(promise)
})
Promise.all(promises).then(() => {
zip.generateAsync({ type: 'blob' }).then((content) => { // 生成二進位制流
FileSaver.saveAs(content, `${filename}.zip`) // 利用file-saver儲存檔案
})
})
}
注意:
由於通過瀏覽器進行打包壓縮,如果檔案過大,或者下載的內容過多,可能導致瀏覽器崩潰。