手把手教你实现超大数据量带进度条导入导出功能(一导入篇)

「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

概述

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

效果演示

20211111_160744.gif

开发流程

  1. 在“上传” 按钮下方编写静态进度条html代码,默认为隐藏状态
1
2
3
css复制代码<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>

效果如下:

微信截图_20211111145713.png

说明:具体使用时通过动态修改内部div的宽度即可实现进度条的变化。

  1. 在文件上传Form表单内添加taskId隐藏框,完整html代码如下:
1
2
3
4
5
6
7
8
css复制代码<button style="width: 100px;" onclick="upload()" id="uploadBtn">上传</button>&nbsp;&nbsp;&nbsp;&nbsp;(仅限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="fileForm" 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用来标识本次上传的任务唯一性
3. 编写文件上传js代码

文件上传开始初始化进度条展示,进度为零,然后开启一个定时任务定时更新上传进度,进度值通过后台接口获取。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
javascript复制代码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
})
},
  1. 编写Java处理上传业务代码
    主要逻辑为,获取文件总条数,然后循环读取记录分批处理,同时更新任务进度(已经处理记录数占总条数的百分比)入内存(集群环境可以存入中间件如redis),key为taskId,value为百分比值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
ini复制代码public void execute() throws Exception{
JSONObject 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());
}

总结

  1. 超大数据文件处理需要分批处理。
  2. 前端组件需要先编写静态页面,然后将其动态化

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%