在 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 多平台公布