主要思路是用线程池池去处理上传任务,并计算上传进度,将进度保存到session中。前端通过一个定时器按固定时间调用获取进度条的百分比,更新进度条进度。
前端相关代码:
<script type="text/javascript" src="${pageContext.request.contextPath }/static/scripts/easyui/jquery.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/static/scripts/easyui/jquery.easyui.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/static/scripts/easyui/easyui-lang-zh_CN.js"></script>
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/static/scripts/easyui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/static/scripts/easyui/themes/icon.css">
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/static/styles/common.css">
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/static/scripts/easyui/themes/demo.css">
<div id="progressDlg" class="easyui-dialog" title="执行进度" style="width: 600px;hight:400px;" data-options="iconCls:'icon-save',closed:true,resizable:true,modal:true,collapsible:true, buttons: '#progressDlgBtns'">
<div style="padding: 10px 60px 20px 60px">
<div id="p" class="easyui-progressbar" style="width:400px;"></div>
</div>
</div>
<div id="progressDlgBtns" style="width: 600px;">
<a href="javascript:void(0)" class="easyui-linkbutton" style="padding: 4px;" οnclick="javascript:$('#progressDlg').dialog('close')">关闭</a>
</div>
//展示进度条
var timerId;
function showCheckProgress(){
$('#progressDlg').dialog('open');
//想要修改进度条的颜色去css文件中去修改
$('#p').progressbar({
value : 0, //设置进度条值 默认0
text : '{value}%' //设置进度条百分比模板 默认 {value}%
//在value改变的时候触发
/*onChange : function (newValue, oldValue) {
console.log('新:' + newValue + ',旧:' + oldValue);
}, */
});
timerId = window.setInterval(getCheckProgress,1000);
}
//通过post请求得到进度
function getCheckProgress(){
var progressUrl = $('#getProgressUrl').val();
//使用JQuery从后台获取JSON格式的数据
$.ajax({
type:"post",//请求方式
url:progressUrl,//发送请求地址
timeout:3000,//超时时间:30秒
dataType:"json",//设置返回数据的格式
//请求成功后的回调函数 data为json格式
success:function(data){
if(data.progressValue>=100){
window.clearInterval(timerId);
$('#dg').datagrid('load');
$('#importBtn').css('display','inline-block');
$('#showProgress').css('display','none');
}
$('#p').progressbar('setValue',data.progressValue);
},
//请求出错的处理
error:function(){
window.clearInterval(timerId);
//alert("请求出错");
}
});
}
文件上传界面:enctype="multipart/form-data"
<div id="importDlg" class="easyui-dialog" title="批量导入" style="width:400px;padding:30px 70px 50px 70px">
<form id="importForm" method="post" enctype="multipart/form-data">
<div style="margin-bottom:20px">
<input id="uploadFile" class="easyui-filebox" name="uploadFile" data-options="prompt:'请选择要导入的文件……'" style="width:100%">
</div>
<div>
<a href="#" id="doImportBtn" class="easyui-linkbutton" style="width:100%">上传</a>
<a href="#" id="cancelImportBtn" class="easyui-linkbutton" style="width:100%">取消</a>
</div>
</form>
</div>
springmvc中对上传文件的通用属性配置:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize"><value>2097152</value></property>
<property name="defaultEncoding"><value>UTF-8</value></property>
</bean>
后台java代码:
/**
* 批量导入贷中记录
*/
@RequestMapping(value = "/action/import")
@ResponseBody
public Result importList(HttpServletRequest request, HttpServletResponse response, @RequestParam MultipartFile uploadFile) {
HttpSession session = request.getSession();
session.setAttribute("progressValue", 0.0);
Result result = new Result();
try {
String fileName = uploadFile.getOriginalFilename();
long size = uploadFile.getSize();//文件大小,字节
long maxSize = 2048000;//2M
String fileType = StringUtils.split(fileName,".")[1];
if(!StringUtils.equals(fileType, "xlsx") && !StringUtils.equals(fileType, "xls")){
result.setCode(0);
result.setMsg("请选择xlsx或xls格式的文件");
return result;
}
if(size > maxSize){
result.setCode(0);
result.setMsg("批量导入的文件大小不能超过2M");
return result;
}
String batchNo = "DZ" + DateUtil.getCurrentDateTime();
List<RcsCreditManageJob> jobList = parseExcel( uploadFile.getInputStream(),session,batchNo);
if(jobList!=null && jobList.size()>3000){
result.setCode(0);
result.setMsg("单次校验记录数不能超过3000");
return result;
}
//先全部批量插入数据库
rcsCreditManageJobServiceImpl.batchAdd(jobList);
result = rcsCreditManageJobServiceImpl.batchCheckCreditIn(session,jobList,batchNo);
} catch (Exception e) {
result.setCode(0);
result.setMsg("批量校验操作失败!");
log.error("【批量校验操作异常】:"+ e.getMessage(),e);
}
return result;
}
/**
* 批量校验
*/
@Override
public Result batchCheckCreditIn(HttpSession session,List<RcsCreditManageJob> jobList,String batchNo) {
Result result = new Result();
try {
int totalSize = jobList.size();
TaskProgress progress = new TaskProgress(totalSize,batchNo);
for (RcsCreditManageJob job : jobList) {
taskExecutor.execute(new dzCheckTask(progress,job,session));
}
result.setCode(1);
result.setMsg("批量校验申请提交完毕!");
} catch (Exception e) {
e.printStackTrace();
result.setCode(0);
result.setMsg("批量校验申请提交失败!");
}
return result;
}
/**
* 执行校验的任务
*/
private class dzCheckTask implements Runnable {
private RcsCreditManageJob job;
private TaskProgress progress;
private HttpSession session;
public dzCheckTask(TaskProgress progress,RcsCreditManageJob job,HttpSession session) {
this.job = job;
this.progress = progress;
this.session=session;
}
public void run() {
try {
EngineCreditInDto ec = new EngineCreditInDto();
ec.setName(job.getUserName());//用户名称
ec.setMobile(job.getMobile());//手机号
ec.setIdCard(job.getCertid());//身份证号
ec.setCurrTime(DateUtil.getCurrentPrettyDateTime());//当前时间
ec.setCallType(job.getCallType());
riskServiceFacade.evaluateCreditInCheck(ec);
//根据表数据
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("execTime", DateUtil.getCurrentDateTime());
paramMap.put("certid", ec.getIdCard());
paramMap.put("batchNo", progress.getTaskNo());
rcsCreditManageJobMapper.updateExcuTimeByMap(paramMap);
//更新进度
session.setAttribute("progressValue", progress.computeProgress());
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 任务进度实体
*/
public class TaskProgress {
private int totalSize;//任务总数
private int completeSize;//任务完成数
private String taskNo;//任务批次
public double taskProgress;//任务进度
public TaskProgress(int totalSize, String taskNo) {
this.totalSize = totalSize;
this.taskNo = taskNo;
}
/**
* 用同步代码块实现
*
* @param money
*/
public double computeProgress() {
synchronized (this) {
BigDecimal b1 = new BigDecimal(Double.toString(this.getTotalSize()));
//没调用一次,完成一笔
int newCompleteSize = this.getCompleteSize() + 1;
this.setCompleteSize(newCompleteSize);
BigDecimal b2 = new BigDecimal(Double.toString(newCompleteSize));
double taskProgress = b2.divide(b1,2,RoundingMode.HALF_UP).multiply(new BigDecimal("100")).doubleValue();
this.setTaskProgress(taskProgress);
return taskProgress;
}
}
public int getTotalSize() {
return totalSize;
}
public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}
public int getCompleteSize() {
return completeSize;
}
public void setCompleteSize(int completeSize) {
this.completeSize = completeSize;
}
public String getTaskNo() {
return taskNo;
}
public void setTaskNo(String taskNo) {
this.taskNo = taskNo;
}
public double getTaskProgress() {
return taskProgress;
}
public void setTaskProgress(double taskProgress) {
this.taskProgress = taskProgress;
}
}
spring mvc中配置线程池的bean
<!-- 异步线程池-->
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心线程数 -->
<property name="corePoolSize" value="10" />
<!-- 最大线程数 -->
<property name="maxPoolSize" value="30" />
<!-- 队列最大长度 >=mainExecutor.maxSize -->
<property name="queueCapacity" value="3000" />
<!-- 线程池维护线程所允许的空闲时间 -->
<property name="keepAliveSeconds" value="300" />
<!-- 线程池对拒绝任务(无线程可用)的处理策略 -->
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
</property>
</bean>
最后是相应获取进度的请求,从session中获取最新的进度:
/**
* 得到处理进度
*/
@RequestMapping(value = "/action/getProgressValue")
@ResponseBody
public Map getProgressValue( HttpSession session) {
double progress = (double) session.getAttribute("progressValue");
Map map = new HashMap();
map.put("progressValue", progress);
return map;
}
比较粗糙的实现,但是能跑能跳,本博客只是自己的工作记录之用,实现方式未必优雅。
如你有更好的方法,欢迎指教。