window.open(`url`, '_self')
使用场景:下载excel文件,后端提供接口,接口返回的是文件流,能够直接使用window.open()
,最简单的方式。html
优势:最简洁;ios
弊端:当参数错误时,或其它缘由致使接口请求失败,这时没法监听到接口返回的错误信息,须要保证请求必须是正确的且能正确返回数据流,否则打开页面会直接输出接口返回的错误信息,体验很差。git
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
属性能够自定义文件名。github
弊端:同window.open()方式同样,没法监听错误信息。ajax
问题:以上两种方式,当在下载.mp3
格式,或者视频文件时,浏览器会直接播放该文件,而达不到直接下载的功能,此时,当下载音视频文件时没法使用以上两种方式。axios
为了解决.mp3
文件下载所带来的问题,经过ajax请求返回Blob
对象,或者ArrayBuffer
对象。后端
以下:经过原生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
对象,同等做用promise
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请求的方式下载文件,能够解决第一、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) }) }
在第3点的基础上,若是要实现批量下载,那能作到的只是连续屡次调用download
方法,这样没法批量集中的下载文件。这个时候就须要可以对已获取到的文件流,进行一个打包的操做,而后一次下载完毕。
这时,须要用到两个库jszip
和 file-saver
完整的思路,经过ajax
获取文件,而后用 jszip
压缩文件, 再用 file-saver
生成文件
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:
http://stuk.github.io/jszip/d...
file-saver:
https://github.com/eligrey/Fi...
贴上完整代码
/** * 获取文件 * @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保存文件 }) }) }
注意:
因为经过浏览器进行打包压缩,若是文件过大,或者下载的内容过多,可能致使浏览器崩溃。