效果展示
实现原理
1:后台同步进程开始下载文件
2:前台使用遮罩效果,使页面不能点击,使用circliful进度条插件(插件源码见后)
3:页面使用JavaScript定时器发送ajax请求刷新进度条数据
4:当进度满100的时候,遮罩消失,文件开始保存到本地
5:后台使用SpringMVC,其中使用session来保存当前下载的数据进度,因为下载文件的进度和刷新的进度是不同的方法,下载文件的方法需要将数据存储到一个地方,供进度条方法来获取数据。
代码如下
HTML样式
#Mask {
position: absolute; top: 0px; filter: alpha(opacity=60); background-color: #333;
z-index: 1002; left: 0px;
opacity:0.5; -moz-opacity:0.5;
}
#Progress{
position: absolute; top: 35%;left:35%;z-index: 2000;
}
#Progress .circle-info{
color:black;
}
<!---Mask是遮罩,Progress是进度条->
<div>
<div id="Mask"></div>
<div id="Progress" data-dimension="250" data-text="0%" data-info="导出进度" data-width="30" data-fontsize="38" data-percent="0" data-fgcolor="#61a9dc" data-bgcolor="#eee"></div>
</div>
js刷新进度请求
因为插件问题,所以需要判断是不是第一次导出数据,如果是第一次,则执行circliful()方法,如果不是,则直接显示原来创建的#Progress对象,否则会显示多个进度条在页面上。各种常见问题的处理都已做判断处理,比如第二次下载,进度一下显示为100%等。直接拷贝进去就可以使用。
//显示进度条
var isFirstExport=true;
function showProgress(){
$("#Mask").css("height",$(document).height());
$("#Mask").css("width",$(document).width());
$("#Mask").show();
if(isFirstExport){
$("#Progress").circliful();
}else{
$("#Progress .circle-text").text("0%");
$("#Progress .circle-info").text("导出进度");
$("#Progress").show();
}
}
//隐藏进度条
function hideProgress(){
$("#Mask").hide();
$("#Progress").hide();
}
function onZipAll() {
//这里开始下载文件
var formData=$("#form_id").serialize();
location.href="${root}/record/v_seal_excel_all.do?"+formData;
//Ajax刷新进度条
showProgress();
window.setTimeout(function(){
var timer=window.setInterval(function(){
$.ajax({
type:'post',
dataType:'json',
url: "${root}/record/flushProgress.do",
success: function(data) {
$("#Progress .circle-text").text(data.percentText);
if(data.curCount===undefined||data.totalCount===undefined){
$("#Progress .circle-info").text("导出进度");
}
else{
$("#Progress .circle-info").text("导出进度:"+data.curCount+"/"+data.totalCount);
}
if(data.percent=="100"){
window.clearInterval(timer);
hideProgress();
}
},
error:function(data){}
});
},200);
},200);
isFirstExport=false;
}
SpringMVC进度条刷新方法
/**
* 进度条刷新,数据从session当中取
*/
@RequestMapping(value = "/flushProgress3.do")
@ResponseBody
public String flushProgress3(HttpServletRequest request) throws Exception
{
HashMap<String,Object> map=null;
try {
HttpSession session = request.getSession();
map=new HashMap<String, Object>();
map.put("totalCount", session.getAttribute("totalCount")); //总条数
map.put("curCount", session.getAttribute("curCount")); //已导条数
map.put("percent", session.getAttribute("percent")); //百分比数字
map.put("percentText", session.getAttribute("percentText")); //百分比文本
} catch (Exception e) {
e.printStackTrace();
}
return JSON.toJSONString(map);
}
###SpringMVC数据下载记录进度方法
@RequestMapping(value = "/download")
public void download( HttpServletRequest request) throws Exception {
//首先需要移除掉session当中的数据,因为如果是第二次下载数据的话,这些数据已经存在
//会导致进度条先是100%的状态,然后才从0%开始
request.getSession().removeAttribute("totalCount");
request.getSession().removeAttribute("curCount");
request.getSession().removeAttribute("percent");
request.getSession().removeAttribute("percentText");
//计算百分比,这里将下载数据的过程省略,需要有总数和当前数,总数应该是不变的,而当前数会不断变化,比如每循环一次加1等,因此会一直不断的写入到session当中,这里重点是计算百分数
++curCount;
double dPercent=(double)curCount/totalCount; //将计算出来的数转换成double
int percent=(int)(dPercent*100); //再乘上100取整
request.getSession().setAttribute("curCount", curCount);
request.getSession().setAttribute("percent", percent); //比如这里是50
request.getSession().setAttribute("percentText",percent+"%");//这里是50%
}
附:插件源码
circliful插件源码,包含一个css和一个js,使用的时候直接引入到html当中即可
CSS
.circliful {
position: relative;
}
.circle-text, .circle-info, .circle-text-half, .circle-info-half {
width: 100%;
position: absolute;
text-align: center;
display: inline-block;
}
.circle-info, .circle-info-half {
color: #999;
}
.circliful .fa {
margin: -10px 3px 0 3px;
position: relative;
bottom: 4px;
}
JS
(function( $ ) {
$.fn.circliful = function(options) {
var settings = $.extend({
// These are the defaults.
foregroundColor: "#556b2f",
backgroundColor: "#eee",
fillColor: false,
width: 15,
dimension: 200,
size: 15,
percent: 50,
animationStep: 1.0
}, options );
return this.each(function() {
var dimension = '';
var text = '';
var info = '';
var width = '';
var size = 0;
var percent = 0;
var endPercent = 100;
var fgcolor = '';
var bgcolor = '';
var icon = '';
var animationstep = 0.0;
$(this).addClass('circliful');
if($(this).data('dimension') != undefined) {
dimension = $(this).data('dimension');
} else {
dimension = settings.dimension;
}
if($(this).data('width') != undefined) {
width = $(this).data('width');
} else {
width = settings.width;
}
if($(this).data('fontsize') != undefined) {
size = $(this).data('fontsize');
} else {
size = settings.size;
}
if($(this).data('percent') != undefined) {
percent = $(this).data('percent') / 100;
endPercent = $(this).data('percent');
} else {
percent = settings.percent / 100;
}
if($(this).data('fgcolor') != undefined) {
fgcolor = $(this).data('fgcolor');
} else {
fgcolor = settings.foregroundColor;
}
if($(this).data('bgcolor') != undefined) {
bgcolor = $(this).data('bgcolor');
} else {
bgcolor = settings.backgroundColor;
}
if($(this).data('animation-step') != undefined) {
animationstep = parseFloat($(this).data('animation-step'));
} else {
animationstep = settings.animationStep;
}
if($(this).data('text') != undefined) {
text = $(this).data('text');
if($(this).data('icon') != undefined) {
icon = '<i class="fa ' + $(this).data('icon') + '"></i>';
}
if($(this).data('type') != undefined) {
type = $(this).data('type');
if(type == 'half') {
$(this).append('<span class="circle-text-half">' + icon + text + '</span>');
$(this).find('.circle-text-half').css({'line-height': (dimension / 1.45) + 'px', 'font-size' : size + 'px' });
} else {
$(this).append('<span class="circle-text">' + icon + text + '</span>');
$(this).find('.circle-text').css({'line-height': dimension + 'px', 'font-size' : size + 'px' });
}
} else {
$(this).append('<span class="circle-text">' + icon + text + '</span>');
$(this).find('.circle-text').css({'line-height': dimension + 'px', 'font-size' : size + 'px' });
}
} else if($(this).data('icon') != undefined) {
}
if($(this).data('info') != undefined) {
info = $(this).data('info');
if($(this).data('type') != undefined) {
type = $(this).data('type');
if(type == 'half') {
$(this).append('<span class="circle-info-half">' + info + '</span>');
$(this).find('.circle-info-half').css({'line-height': (dimension * 0.9) + 'px', });
} else {
$(this).append('<span class="circle-info">' + info + '</span>');
$(this).find('.circle-info').css({'line-height': (dimension * 1.25) + 'px', });
}
} else {
$(this).append('<span class="circle-info">' + info + '</span>');
$(this).find('.circle-info').css({'line-height': (dimension * 1.25) + 'px', });
}
}
$(this).width(dimension + 'px');
var canvas = $('<canvas></canvas>').attr({ width: dimension, height: dimension }).appendTo($(this)).get(0);
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var degrees = percent * 360.0;
var radians = degrees * (Math.PI / 180);
var radius = canvas.width / 2.5;
var startAngle = 2.3 * Math.PI;
var endAngle = 0;
var counterClockwise = false;
var curPerc = animationstep === 0.0 ? endPercent : 0.0;
var curStep = Math.max(animationstep, 0.0);
var circ = Math.PI * 2;
var quart = Math.PI / 2;
var type = '';
var fill = false;
if($(this).data('type') != undefined) {
type = $(this).data('type');
if(type == 'half') {
var startAngle = 2.0 * Math.PI;
var endAngle = 3.13;
var circ = Math.PI * 1.0;
var quart = Math.PI / 0.996;
}
}
if($(this).data('fill') != undefined) {
fill = $(this).data('fill');
} else {
fill = settings.fillColor;
}
//animate foreground circle
function animate(current) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(x, y, radius, endAngle, startAngle, false);
context.lineWidth = width - 1;
// line color
context.strokeStyle = bgcolor;
context.stroke();
if(fill) {
context.fillStyle = fill;
context.fill();
}
context.beginPath();
context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
context.lineWidth = width;
// line color
context.strokeStyle = fgcolor;
context.stroke();
if (curPerc < endPercent) {
curPerc += curStep;
requestAnimationFrame(function () {
animate(Math.min(curPerc, endPercent) / 100);
});
}
}
animate(curPerc / 100);
});
};
}( jQuery ));