概述
数据入导出功能是程序员开发中比较常见的功能,操作 导入导出 的人为业务人员,大批量数据的导入导出往往需要长时间的等待,为了让操作人员对导入导出进度直观可见,为导入导出功能添加进度条就是的该功能的使用体验有值得提升。
效果演示

开发流程
- 在“上传” 按钮下方编写静态进度条html代码,默认为隐藏状态
<div id="progress" style="height:20px;width:100%;background: #efefef;border:1px solid #eee;border-radius:10px;display:none;">
<div class="bar" style="background: green;width:10%;height: 100%;border-radius:10px; line-height :20px;">0%</div>
</div>
效果如下:

说明:具体使用时通过动态修改内部div的宽度即可实现进度条的变化。
- 在文件上传Form表单内添加taskId隐藏框,完整html代码如下:
<button style="width: 100px;" onclick=" Upload ()" id="uploadBtn">上传</button> (仅限 CSV 格式)<br><br>
<div id="progress" style="height:20px;width:100%;background: #efefef;border:1px solid #eee;border-radius:10px;">
<div class="bar" style="background: green;width:10%;height: 100%;border-radius:10px;line-height:20px;">0%</div>
</div>
<form id=" file Form" enctype="multipart/form-data">
<input type="file" accept="text/csv" style="display: none;" id="fileInput" onchange="onFileChange()" name="upLoadFile">
<input type="hidden" id="taskId" name="taskId">
</form>
说明: 添加taskId用来标识本次上传的任务唯一性
- 编写文件上传js代码
文件上传开始初始化进度条展示,进度为零,然后开启一个定时任务定时更新上传进度,进度值通过后台接口获取。代码如下:
function upload(){
$('#fileInput').click()
}
function onFileChange(){
var bar =$('#progress').show().find('.bar')
bar.text('0%')
bar.css({width:'0%'});
var taskId = ''+ new Date().getTime()
$('#taskId').val(taskId)
timer = setInterval(function(){
$.ajax({
type:'post',
url:'getProgress',
data:{taskId:taskId},
dataType: "json",
}).success(function(data){
if(data.result){
bar.text(data.value + '%')
bar.css({width:data.value + '%'})
}
}).error(function(e){
})
},2000);
$.ajax({
type:'post',
url:'upload',
data:new FormData($('#fileForm')[0]),
cache: false,
processData: false,
contentType: false,
}).success(function(data){
clearInterval(timer)
$('#progress').hide().find('.bar').css({width:'0%'})
// do some things
}).error(function(e){
clearInterval(timer)
$('#progress').hide().find('.bar').css({width:'0%'})
// do some things
})
},
- 编写 Java 处理上传业务代码
主要逻辑为,获取文件总条数,然后循环读取记录分批处理,同时更新任务进度(已经处理记录数占总条数的百分比)入内存(集群环境可以存入中间件如redis),key为taskId,value为百分比值。
public void execute() throws Exception {
json Object jsonObject = new JSONObject();
jsonObject.put("result",true);
jsonObject.put("msg","成功");
UploadFile upLoadFile = getFile("upLoadFile","/", integer .MAX_VALUE);
final File file = upLoadFile.getFile();
final int lineNumber;
// 获取文件总条数
try (final FileReader in1 = new FileReader(file); final LineNumberReader lineNumberReader = new LineNumberReader(in1)){
lineNumberReader.skip(file.length());
lineNumber = lineNumberReader.getLineNumber();
}
final String username = getUsername();
final String taskId = getPara("taskId");
int sum = 0;
final Pattern compile = Pattern.compile("1\d{10}");
try(final FileReader in = new FileReader(file); final BufferedReader reader = new BufferedReader(in);){
String str;
List<String> mobileList =new ArrayList<>();
while ((str = reader.readLine()) != null){
String mobile = str.trim().split(",")[0];
if(!compile.matcher(mobile).matches()){
jsonObject.put("result",false);
jsonObject.put("msg","手机号格式不正确("+mobile+")");
continue;
}
mobileList.add(mobile);
if(mobileList.size() == 100){
int num = remoteUploadUser(mobileList,username);
mobileList.clear();
}
sum ++;
taskProgressMap.put(taskId,sum * 100/lineNumber);
}
if(!mobileList.isEmpty()){
int num = remoteUploadUser(mobileList,username);
mobileList.clear();
}
}catch (Exception e){
jsonObject.put("result",false);
jsonObject.put("msg","系统错误:" + e.getMessage());
}
jsonObject.put("count",count);
renderJson(jsonObject.toJSONString());
}
public void getProgress(){
JSONObject jsonObject = new JSONObject();
jsonObject.put("result",true);
jsonObject.put("msg","成功");
final String taskId = getPara("taskId");
final Integer integer = taskProgressMap.get(taskId);
if(integer == null){
jsonObject.put("result",false);
jsonObject.put("msg","失败");
}
jsonObject.put("value",integer);
renderJson(jsonObject.toJSONString());
}
总结
- 超大数据文件处理需要分批处理。
- 前端组件需要先编写静态页面,然后将其动态化