在Web开发中,前端下载文件是一个常见的需要。依据文件起源和下载方式的不同,前端下载文件能够采纳多种计划。上面将介绍几种常见的前端下载文件计划。
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 对象。
获取文件
如下:通过原生 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 中存在的弊病,当申请谬误时或捕捉到错误信息
保留文件
当获取到文件后,这时须要保留文件
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 = ({ method = "GET", url }) => { return new Promise((resolve) => { const xhr = new XMLHttpRequest(); xhr.open(method, 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); });};
用后盾的 filename 的话,能够从 header 的 content-disposition 中去获取。
4、如何实现批量下载,且打包文件
在第 3 点的根底上,如果要实现批量下载,那能做到的只是间断屡次调用 download 办法,这样无奈批量集中的下载文件。这个时候就须要可能对已获取到的文件流,进行一个打包的操作,而后一次下载结束。
这时,须要用到两个库 jszip 和 file-saver
残缺的思路:通过 ajax 获取文件,而后用 jszip 压缩文件, 再用 file-saver 生成文件
- 获取文件 同上(第 3 点)
- 打包文件
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保留文件 }); });};
残缺代码
/** * 获取文件 * @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保留文件 }); });};
留神:因为通过浏览器进行打包压缩,如果文件过大,或者下载的内容过多,可能导致浏览器解体。
总结
综上所述,前端下载文件的计划多种多样,开发者应依据具体需要和场景抉择适合的计划。对于简略的动态文件下载,能够间接应用<a>
标签;对于动静生成的文件或须要通过Ajax申请获取的文件,能够应用JavaScript或第三方库来实现下载性能。无论抉择哪种计划,都应留神文件的安全性、兼容性和性能问题,以确保良好的用户体验。
本文由mdnice多平台公布