在项目中经常会有下载文件的需求,大多数时候我都是一个a标签或者window.location.href = "";一行代码搞定,但是最近有一个文件下载需求,因为下载的文件有点大,所以速度确实慢,基本以10m+才能下载下来,所以做了一个进度条,然后问题来了,怎么可以监听文件是否下载完成,然后取消掉进度条。。。
开始折腾了半天的jquery,整不成,决定用原生ajax写,其中也自己整理了几种方法,特此记录哈
第一种方法:原生ajax+FileReader
$("#execlTo_btn").on("click",function(){
var itime = 0;
$(btn).attr("disabled", "disabled");
$(btn).html(`正在下载<i style="color:blueviolet;">${itime}</i>`);
var hurl = `yjToExel?yjjb=123`;
var xhr = new XMLHttpRequest();
xhr.open('get', hurl, true);
xhr.responseType = "blob";
xhr.onload = function() {
if (this.status === 200) {
var blob = this.response;
var reader = new FileReader;
reader.readAsDataURL(blob);
reader.onload = function(e) {
var headerName = xhr.getResponseHeader("Content-disposition");
var fileName = decodeURIComponent(headerName).substring(20);
var a = document.createElement("a");
a.download = fileName;
a.href = e.target.result;
$("body").append(a);
a.click();
$(a).remove();
clearTimeout(downloadTimer);
$(btn).html("导出Execl");
$(btn).removeAttr("disabled");
}
}
}
xhr.send();
var downloadTimer = setInterval(() => {
$(btn).children("i").text(++itime);
}, 1000);
})
代码解析:首先点击下载按钮肯定先把按钮给禁用了,等下载完成再运行点击,这段代码的关键在于获取reponse的数据然后通过reader.readAsDataURL(blob);把字节流转为base64,然后再下载下来,这样最大的缺点是数据量太大的时候,浏览器内存会爆,主要是字节流转bese64后数据字符猛增导致的
第二种方法:window.open()
var itime = 0;
$(btn).attr("disabled", "disabled");
$(btn).html(`正在下载<i style="color:blueviolet;">${itime}</i>`);
loadExeclBtn();
var hurl = `yjToExel?yjjb=123`;
let net = window.open(hurl);
net.addEventListener("beforeunload", (e) => {
clearTimeout(downloadTimer);
$(btn).html("导出Execl");
$(btn).removeAttr("disabled");
});
var downloadTimer = setInterval(() => {
$(btn).children("i").text(++itime);
}, 1000);
代码解析:首先一眼看去代码是简洁很多,而且进行大数据量的下载也是没问题了,进行数据监听也是很方便,但是这个方法最大的问题在于点击下载后会新打开一个浏览器窗口,给我感觉相当不好,故弃之
第三种方法:原生ajax+分页查询+递归下载
// exel导出按钮
//总条数
var datanum = 0;
//请求地址数组
var getUrl = [];
$("#execlTo_btn").on("click", function() {
getUrl = [];
var btn = this;
$.get("getYjCount", {
"yjjb": 123
}, function(data) {
if (data && data < 50000) {
if (data > 5000) {
$("#queding_btn").show();
$("#guanbi_btn").show();
$("#fileListNum").text(data);
$("#fileList_Modal").modal("show");
datanum = Math.ceil(data / 5000);
let html = "";
for (var i = 0; i < datanum; i++) {
if (data - i * 5000 > 5000) {
html += `<li data-pagelist="${i}">${i*5000+1}条-${(i+1)*5000}条 <button data-ts="${i*5000+1}-${(i+1)*5000}条" id="to_execl_btn_${i}" class="btn btn-default execlTo_btn">未下载</button></li>`;
} else {
html += `<li data-pagelist="${i}">${i*5000+1}条-${data}条 <button data-ts="${i*5000+1}-${data}条" id="to_execl_btn_${i}" class="btn btn-default execlTo_btn">未下载</button></li>`;
}
getUrl.push(i);
}
$("#fileList_ul").html(html);
} else {
//第三个方法cyx
var hurl = `yjToExel?nowpage=999&yjjb=123`;
loadToExeclData(hurl, btn, "导出Execl", "导出数据.xlsx");
}
} else {
toastr.warning("最大数据下载量5万!");
}
});
});
//点击确定按钮,只有在5000条以上的时候才有这个事件
$("#queding_btn").on("click", function() {
if (getUrl && getUrl.length > 0) {
$("#queding_btn").hide();
$("#guanbi_btn").hide();
var hurl = `yjToExel?nowpage=0&yjjb=123`;
loadToExeclData(hurl, document.getElementById("to_execl_btn_0"), "已下载", "预警导出数据(1-5000条).xlsx");
}
});
function loadToExeclData(hurl, btn, btnName, fileName){
var itime = 0;
$(btn).attr("disabled", "disabled");
$(btn).html(`正在下载<i style="color:blueviolet;">${itime}</i>`);
//进度条的,不贴代码了,自己可以随便写
loadExeclBtn(btn);
var xhr = new XMLHttpRequest();
xhr.open('get', hurl, true);
xhr.responseType = "blob";
xhr.onload = function() {
if (this.status === 200) {
var res = this.response;
const blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
const a = document.createElement('a');
a.download = fileName;
a.href = URL.createObjectURL(blob);
a.click();
$(a).remove();
clearTimeout(downloadTimer);
$(btn).html(btnName);
$(btn).removeAttr("disabled");
if (getUrl && getUrl.length > 0) {
getUrl.splice(0, 1);
console.log(getUrl);
if (getUrl.length > 0) {
var loadBtn = document.getElementById(`to_execl_btn_${getUrl[0]}`);
var loadUrl = `yjToExel?nowpage=${getUrl[0]}&yjjb=123`;
console.log(loadUrl);
loadToExeclData(loadUrl, loadBtn, "已下载", `预警导出数据(${loadBtn.dataset.ts}).xlsx`);
}else{
$("#guanbi_btn").show();
}
}
}
}
xhr.send();
var downloadTimer = setInterval(() => {
$(btn).children("i").text(++itime);
}, 1000);
}
代码解析:这个方法是我最终使用的方法,咋一看,代码量虽是猛然剧增,其实是把原来的方法给剥离成了两个方法,主要的思路是大于5000条数据就分次查询然后递归下载,下载之后的数据直接使用字节流数据,可以节省很多的内存空间,下载大数据量的速度也有了质的提升,后端的代码也不贴了,就是一个分页查询,然后返回byte[]字节流,很简单