1、需求背景
我们的产品有个下载导出成员信息表单的功能,但当数据量大的时候,处理时间较长,客户希望能有个进度条显示下载的进度,在焦急等待的时候,有个心理安慰
2、开发工具及框架
SpringBoot + Maven +JDK8
3、整体思路
数据传输的主要途径:request.session
(1)前端页面编写调试
(2)后端最短路径传输模拟数据,调试
(3)后端业务逻辑处理,传输真实数据
(4)前端操作、页面优化
3\下载完2秒左右关闭进度条
进度条核心标签:progress
js定时器:setInterval、setTimeout
思路:
① 如果你的项目和我一样,要运行后才能打开前端页面的话,建议自己新建一个项目,把前端页面调好后,再放到原项目中
② 调试过程中,如果你的百分数直接从0到100,没有中间的数字,一定是不对的,继续调整
③ 注意判断条件添加的位置:如果你的提示语显示顺序和百分数有关,那就一定要小心谨慎了,这里很有可能弄错
(1)测试代码编写:为便于调试,我把js代码也一并写到了html页面中(以下代码浏览器打开可直接运行)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>进度调测试5</title>
<script>
function download() {
var pro=document.getElementsByTagName( "progress" )[0];
pro.style.display = 'block';
var progressValue = document.querySelector('#progress-value');
progressValue.style.display = 'block';
var value = 0;
setInterval(function() {
if (value < 100){
value++;
pro.value = value;
progressValue.innerText = value + '%';
}else {
progressValue.innerText = '下载完成';
setInterval(function (){
location.replace(location.href);
},2000);
}
}, 20);
};
</script>
</head>
<body>
<br/>
<span id="progress-value" hidden="hidden">0</span>
<progress value= "0" max= "100" hidden="hidden">您的浏览器不支持progress标签</progress>
<br/>
<button onclick="download()">点击下载</button>
</body>
</html>
(2)测试代码运行没问题后,挪入项目中,修改的点有:
//html代码:
<div style="position: relative;width: 175px;height: 100%;margin-left: calc(50% - 50px);display: inline-block">
<progress value= "0" max= "100" hidden="hidden">
您的浏览器不支持progress标签
</progress>
<span id="progress-value" hidden="hidden" style="position: absolute;top:-4px;right:5px"></span>
<p id="progress-tips" hidden="hidden" style="text-align: center;width: 122px"></p>
</div>
//js代码:在下载按钮的点击函数中调用以下函数:
function progressBar() {
//显示进度条、百分数、提示语
var pro = document.getElementsByTagName("progress")[0];
pro.style.display = 'block';
var progressValue = document.querySelector('#progress-value');
progressValue.style.display = 'block';
var progressTips = document.querySelector('#progress-tips');
progressTips.style.display = 'block';
progressTips.innerText = '';
var value = 0;
//设置定时器,每隔一段时间获取requestSession中的百分数
var time = setInterval(function () {
$.ajax({
async: true,
type: 'post',
dataType: 'json',
url: ctx + "/member/getPercent",
//获取百分数成功,修改相关值
success: function (data) {
value = data.percent;
pro.value = value;
progressValue.innerText = value + '%';
progressTips.innerText = '下载中';
//下载完成后,请求刷新百分数,并在2秒后关闭标签
if (value == "100"){
progressTips.innerText = '下载完成!';
setTimeout(function (){
$.ajax({
type: 'post',
dataType: 'json',
url: ctx + "/member/flushPercent",
success:function (){
pro.style.display = 'none';
progressValue.style.display = 'none';
progressTips.style.display = 'none';
pro.value = 0;
progressValue.innerText = '';
}
});
},2000);
clearInterval(time);
return ;
}
},
});
}, 10);
};
目的:主要是测试前端是否能成功获取到后端传来的数据并展示,所以直接选取一个最短路径来传输数据,在controller中新增如下两个接口。
这里在控制台打印percent,主要是方便查看程序运行中接口的调用情况(作用很大!!!不要省略!!!)
(这时不建议大家直接将业务层计算的百分数传给前端,增加数据传输流程,如果出问题的话,排查起来更慢。但嫌麻烦的话,也可以直接跳到第3步。)
//接口一:设置并获取百分数
HashMap<String, Object> map = new HashMap<>();
try {
request.getSession().setAttribute("percent",50);
Object percent = request.getSession().getAttribute("percent");
map.put("percent",percent);
} catch (Exception e) {
e.printStackTrace();
}
String per = JSON.toJSONString(map);
System.out.println("2、获取到的百分数为" + per);
//接口二:刷新百分数
request.getSession().setAttribute("percent",null);
Object percent = request.getSession().getAttribute("percent");
System.out.println("3、刷新百分数为" + percent);
第三步没问题后,我们就直接处理实际的业务逻辑啦,计算真实的百分数,并拉通数据传输的通道
(1)本项目中的进度条主要是用来体现表格的下载进度,而表格下载的主要代码逻辑就在于,将成员数据写入表格里的这个过程。所以我这里的百分数,计算的方式是:
百分数 = 当前写入成员的序号 / 总成员数 * 100;
代码如下:
//设置百分数
//list是成员集合
for (int i = 0; i < list.size(); i++) {
Double percent = (i + 1) / (double)list.size() * 100;
//这里对小数采用向下取整,例如99.5会取整为99
percent = Math.floor(percent);
request.getSession().setAttribute("percent",percent);
System.out.println("1、设置百分数为" + percent);
}
(2)修改controller的两个接口
//获取百分数
@ResponseBody
@RequestMapping(value = "/getPercent")
public String getPercent(HttpServletRequest request) {
HashMap<String, Object> map = new HashMap<>();
try {
Object percent = request.getSession().getAttribute("percent");
map.put("percent",percent);
} catch (Exception e) {
e.printStackTrace();
}
String per = JSON.toJSONString(map);
System.out.println("2、获取到的百分数为" + per);
return per;
}
//刷新百分数
@ResponseBody
@RequestMapping(value = "/flushPercent")
public void flushPercent(HttpServletRequest request) {
request.getSession().setAttribute("percent",null);
Object percent = request.getSession().getAttribute("percent");
System.out.println("3、刷新百分数为" + percent);
}
(3)运行项目,测试。调试过程中,不要忘了看控制台打印的输出语句,对排查bug很有用哦
这里主要就是前端页面的优化啦~
我这个项目里,存在以下一个关键问题:
成员信息表格下载,后端逻辑处理主要是3个部分:
(1)根据用户选择的成员,遍历到数据库中查找成员信息;
(2)将成员信息写入到表格里
(3)将表格写入到流中,导出文件
过程(1)(2)都很耗时间,然而进度条只能反映过程(2)的进度,所以过程(1)还是需要用户等待。这时前端页面的优化为:在前端页面添加相关提示,提醒用户文件正在下载。
如果你从头到这里都没调通的话,建议就先只做进度条和百分数的展示(省去提示语),减少一些前端的判断。因为前后端拉通调的话,数据传输、判断的先后等细节都可能导致成功与你擦肩而过(别问我为什么知道)
最后,如果有什么没讲清楚或可以完善优化地方,欢迎一起讨论;看完有收获就点个赞吧
bye~
参考文档:
progress bar(可参考前端页面进度条的实现)
SpringBoot如何实现一个实时更新的进度条的示例代码(可参考进度条实现的整体思路)