1、通过 window.open()打开新页面下载文件
1 2 3 4
| window.open( `/ajax/common/exportAudioRejectReason?${parseParams(params)}`, '_self' )
|
使用场景:下载 excel 文件,后端提供接口,接口返回的是文件流,可以直接使用window.open()
,最简单的方式。
优点:最简洁;
弊端:当参数错误时,或其它原因导致接口请求失败,这时无法监听到接口返回的错误信息,需要保证请求必须是正确的且能正确返回数据流,不然打开页面会直接输出接口返回的错误信息,体验不好。
2、通过a
标签打开新页面下载文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export const exportFile = (url, fileName) => { const link = document.createElement('a') const body = document.querySelector('body')
link.href = url link.download = fileName
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
对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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
对象,同等作用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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)、保存文件
当获取到文件后,这时需要保存文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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) link.download = filename
link.style.display = 'none' body.appendChild(link)
link.click() body.removeChild(link)
window.URL.revokeObjectURL(link.href) } }
|
为了解决 IE(ie10 - 11)和 Edge 无法打开 Blob URL 链接的方法,微软自己有一套方法window.navigator.msSaveOrOpenBlob(blob, filename)
,打开并保存文件,以上代码做了简单的兼容,navigator.msSaveBlob(blob, filename)
是直接保存。注意,此为非标准功能,详情请查看相关文档。
以下为完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 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) link.download = filename
link.style.display = 'none' body.appendChild(link)
link.click() body.removeChild(link)
window.URL.revokeObjectURL(link.href) } }
export const download = (url, filename = '') => { getBlob(url).then((blob) => { saveAs(blob, filename) }) }
|
4、如何实现批量下载,且打包文件
在第 3 点的基础上,如果要实现批量下载,那能做到的只是连续多次调用download
方法,这样无法批量集中的下载文件。这个时候就需要能够对已获取到的文件流,进行一个打包的操作,然后一次下载完毕。
这时,需要用到两个库jszip
和 file-saver
。
完整的思路,通过ajax
获取文件,然后用 jszip
压缩文件, 再用 file-saver
生成文件。
(1)、获取文件
同第 3 点中的第(1)点
(2)、打包文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| export const download = () => { const urls = ['url', 'url'] const zip = new JSZip() const cache = {} const promises = [] urls.forEach((item) => { const promise = getBlob(item).then((data) => { 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`) }) }) }
|
关于 jszip、file-saver 的更多详情如下
jszip:
jszip
examples
file-saver:
file-saver
贴上完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
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() }) }
export const download = (url, filename = '') => { if (!url.length > 0) return const data = url const zip = new JSZip() const cache = {} const promises = [] data.forEach(item => { const promise = getBlob(item.url).then(data => { 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`) }) }) }
|
注意:
由于通过浏览器进行打包压缩,如果文件过大,或者下载的内容过多,可能导致浏览器崩溃。