问题描述:在项目中写了个文件流形式的公共下载函数,但是发现下载大文件时,出现了“假死”现象,点击下载之后半天没反应,于是又改成a标签点击链接下载,但是遇到跨域文件无法重命名问题。
js文件下载大体分为两种:
一、将文件以文件流形式下载到浏览器
将文件以文件流形式下载浏览器之后,再以此文件流为a标签中的href,模拟点击下载。
缺点:
- 这种方式下载小文件没有啥大问题,但是如果下载大文件,就会出现用户点击之后,浏览器半天没有反应,直到文件下载完成,浏览器才会显示文件下载完成(因为是将文件流下载完成之后,才模拟一个a标签,点击下载),用户则可能认为点击无效,则多次点击下载,导致下载很多个文件,给用户体验不好。
- 如果文件足够大,比方说十几G的文件下载,则可能会导致浏览器崩溃,因为文件流可能超出了内存。这个只是个人推测,没有实际测试。
网上已有大量该下载代码,这里贴一个网上的代码:
/**
* 文件下载重命名,根据依赖file-saver改写
*/
/**
* 获取 blob
* @param {String} url 目标文件地址
* @return {cb}
*/
export function getBlob(url, cb) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "blob";
xhr.onload = function () {
if (xhr.status === 200) {
cb(xhr.response);
}
};
xhr.send();
}
/**
* 保存
* @param {Blob} blob
* @param {String} filename 想要保存的文件名称
*/
export function saveAs(blob, filename) {
if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename);
} else {
var link = document.createElement("a");
var body = document.querySelector("body");
link.href = window.URL.createObjectURL(blob);
link.download = filename;
// fix Firefox
link.style.display = "none";
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
}
/**
* 下载
* @param {String} url 目标文件地址
* @param {String} filename 想要保存的文件名称
*/
export function renameDownload(url, filename) {
getBlob(url, function (blob) {
saveAs(blob, filename);
});
}
二、直接模拟a标签点击下载
a标签点击链接下载。
缺点:
- 会出现跨域点击下载文件命名失效;
- txt,图片,pdf等文件格式浏览器直接打开,而不是下载问题。
解决方案:
但是这些问题可以通过添加header来解决,但具体是否所有浏览器都兼容,以及安全问题,本人没有深究。
下边贴代码:
/**
* 下载
* @param {String} fileUrl 目标文件地址
* @param {String} filename 想要保存的文件名称
*/
export function renameDownload(fileUrl, fileName) {
let link = document.createElement("a");
//这里属性涉及到跨域问题后,设置无效
// = fileName;
link.style.display = "none";
//response-content-type=application/octet-stream,这里是为了防止图片,pdf,txt文件直接被浏览器打开
//response-content-disposition的设置,解决跨域文件名重命名无效问题,encodeURIComponent防止文件名带特殊字符(例如&等)导致下载文件名出问题
link.href =
fileUrl +
"?response-content-type=application/octet-stream&response-content-disposition=filename=" +
${encodeURIComponent(fileName)};
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(link.href);
document.body.removeChild(link, fileName);
}