实现思路
网页进度条更新有两种方式
1、轮询请求服务端、返回进度
2、服务端实时推送进度数据给客户端
轮询方式的实现方法,服务端在执行的过程中将进度数据保存再session中,客户端调用的时候从session中取出来,然后更新进度条的数据,从而改变进度条。
服务端实时推送,客户端请求执行任务的时候连接到websocket,服务端在执行的过程中将进度数据通过websocket的方式实时推送到客户端,客户端拿到数据后改变进度条
轮询请求方式
后端实现代码
/**
* 任务执行方法(在实际应用中要改成自己的逻辑代码)
*Title: execute
*author:liuxuli
*Description:
* @param request
* @return
*/
@RequestMapping(value = "execute")
public String execute(HttpServletRequest request) {
for (int i = 0; i <= 100; i++) {
//for循环只是个例子,在实际业务中百分比要进行实际的计算,这里的i就当是百分比
//将进度值存储到session中
request.getSession().setAttribute("processvalue", i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "success";
}
/**
* 获取session中的进度值
*Title: getprocess
*author:liuxuli
*Description:
* @param request
* @return
*/
@RequestMapping(value = "getprocess")
public Object getprocess(HttpServletRequest request) {
//从session将执行进度值取出来并返回给用户
return request.getSession().getAttribute("processvalue");
}
网页端使用layui的进度条进行实现
<div class="layui-form-item">
<div class="layui-input-block">
<div class="layui-progress layui-progress-big" lay-showPercent="yes" lay-filter="executeprogress" style="width: 80%;">
<div id="percentdiv" class="layui-progress-bar layui-bg-green" lay-percent="0%"></div>
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit="" id="submitbtn" lay-filter="demo1">执行</button>
</div>
</div>
var element;
layui.use('element', function(){
element = layui.element;
});
//定义定时器
var timer;
//开始执行任务
$('#submitbtn').click(function(){
//进度条设置为百分之0
element.progress('executeprogress', '0%');
//轮询获取进度条数据
getProcessvalue();
//请求后台
$.post('${pageContext.request.contextPath }/excute',function(data){
//执行完成进度条设置为百分之百
element.progress('executeprogress', '100%');
//关闭定时器
clearInterval(timer);
});
});
//轮询请求进度数据
function getProcessvalue(){
//1秒请求一次进度条的数据
timer = setInterval(function () {
$.post('${pageContext.request.contextPath }/getprocess',function(data){
//更新进度条
element.progress('executeprogress', data+'%');
});
}, 1000);
}
实时推送方式
1、创建websocket
/**
* 创建websocketsession类,SessionConfigurator是降httpsession放入到websocket的用户属性中,
* 如果使用springwebsocket就不需要这些配置了,因为spring的websocket框架已经配置好了
* Title: TestWebsocket
* Description:
* @author liuxuli
* @date 2020年6月29日
*/
@ServerEndpoint(value = "/testwebsocket",configurator = SessionConfigurator.class)
public class TestWebsocket {
/**
* 存储所有在线的用户(观察者模式)
*/
private static Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();
/**
* 当用户连接到websocket,将该用户进行记录
*Title: onopen
*author:liuxuli
*Description:
* @param session
* @param config
*/
@OnOpen
public void onopen(Session session,EndpointConfig config) {
HttpSession httpsession = (HttpSession) session.getUserProperties().get(HttpSession.class.getName());
System.out.println("连接成功");
sessions.put(httpsession.getId(), session);
}
/**
* 当用户退出连接后,将该用户进行删除
*Title: onClose
*author:liuxuli
*Description:
* @param session
*/
@OnClose
public void onClose(Session session) {
HttpSession httpsession = (HttpSession) session.getUserProperties().get(HttpSession.class.getName());
System.out.println("连接关闭");
sessions.remove(httpsession.getId());
}
@OnError
public void OnError(Throwable error,Session session) {
System.out.println("错误");
error.printStackTrace();
}
/**
* 向指定的用户发送消息
*Title: sendMessage
*author:liuxuli
*Description:
* @param httpsession 必须使用这个参数,如果不使用此参数来区分接收的用户,既浪费服务器资源,还能使所有的用户都能收到消息
* @param text 发送的消息
*/
public static void sendMessage(HttpSession httpsession,String text) {
Session session = sessions.get(httpsession.getId());
if (text != null && text.length() > 0) {
session.getAsyncRemote().sendText(text);
}
}
}
SessionConfigurator类代码:
/**
* 用来获取客户端的sessionid 从而绑定到websocket
* Title: SessionConfigurator
* Description:
* @author liuxuli
* @date 2020年6月29日
*/
public class SessionConfigurator extends Configurator{
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
//获取到客户端的session
HttpSession session = (HttpSession) request.getHttpSession();
//将session当如到websocket的用户属性中
sec.getUserProperties().put(HttpSession.class.getName(), session);
super.modifyHandshake(sec, request, response);
}
}
//初始化layui,进度条依赖element
var element;
layui.use('element', function(){
element = layui.element;
});
//执行任务
$('#submitbtn').click(function(){
//将进度条设置为05
element.progress('executeprogress', '0%');
//连接到websocket
connect();
//请求服务端进行执行任务
$.post('${pageContext.request.contextPath }/excute.do',function(data){
//服务端返回,任务执行完毕,关闭连接
close();
});
});
var ws;
//连接到websocket
function connect(){
if ("WebSocket" in window) {
ws = new WebSocket("${websocketserver}/testwebsocket");
ws.onopen = function() {
console.log("连接成功");
};
ws.onmessage = function (evt) {
//接收服务端的推送信息,改变进度条
element.progress('executeprogress', evt.data+'%');
};
ws.onclose = function() {
// 关闭 websocket
console.log("连接已关闭...");
};
}
else{
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
}
}
//关闭websocket
function close(){
ws.close();
}
服务端的执行方法
/**
* 任务执行方法(在实际应用中要改成自己的逻辑代码)
*Title: execute
*author:liuxuli
*Description:
* @param request
* @return
*/
@RequestMapping(value = "execute")
public String execute(HttpServletRequest request) {
for (int i = 0; i <= 100; i++) {
//for循环只是个例子,在实际业务中百分比要进行实际的计算,这里的i就当是百分比
//推送给用户
TestWebsocket.sendMessage(request.getSession(), String.valueOf(i));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "success";
}