284 lines
9.0 KiB
Markdown
284 lines
9.0 KiB
Markdown
|
# EasyExcel 上传(导入)
|
|||
|
|
|||
|
以人口(Population)导入为例:
|
|||
|
|
|||
|
1. 包含 **Excel实体类**、**错误实体类**、**导入监听器**,其中 **错误实体类** 非必须。
|
|||
|
|
|||
|
2. 如果不需要返回错误,可忽略输出错误文件的代码。
|
|||
|
|
|||
|
## Excel实体类
|
|||
|
|
|||
|
```java
|
|||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
|||
|
|
|||
|
public class PopulationExcel {
|
|||
|
|
|||
|
@ExcelProperty(index = 0)
|
|||
|
private String name;
|
|||
|
@ExcelProperty(index = 1)
|
|||
|
private String idCard;
|
|||
|
@ExcelProperty(index = 2)
|
|||
|
private String areaCode;
|
|||
|
@ExcelProperty(index = 3)
|
|||
|
private String homeAddress;
|
|||
|
|
|||
|
// get set
|
|||
|
}
|
|||
|
|
|||
|
```
|
|||
|
|
|||
|
## 错误试题类
|
|||
|
|
|||
|
```java
|
|||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
|||
|
|
|||
|
public class PopulationExcelError {
|
|||
|
|
|||
|
@ExcelProperty(index = 0)
|
|||
|
private String name;
|
|||
|
@ExcelProperty(index = 1)
|
|||
|
private String idCard;
|
|||
|
@ExcelProperty(index = 2)
|
|||
|
private String areaCode;
|
|||
|
@ExcelProperty(index = 3)
|
|||
|
private String homeAddress;
|
|||
|
@ExcelProperty(index = 4)
|
|||
|
private String reason;
|
|||
|
|
|||
|
// get set
|
|||
|
}
|
|||
|
|
|||
|
```
|
|||
|
|
|||
|
## 监听器
|
|||
|
|
|||
|
```java
|
|||
|
import com.alibaba.excel.context.AnalysisContext;
|
|||
|
import com.alibaba.excel.event.AnalysisEventListener;
|
|||
|
|
|||
|
import java.util.ArrayList;
|
|||
|
import java.util.List;
|
|||
|
|
|||
|
public abstract class PopulationExcelListener extends AnalysisEventListener<PopulationExcel> {
|
|||
|
|
|||
|
private static final Integer MAX_READ_COUNT = 100;
|
|||
|
private List<PopulationExcel> populationExcels = new ArrayList<>();
|
|||
|
|
|||
|
@Override
|
|||
|
public void invoke(PopulationExcel populationExcel, AnalysisContext analysisContext) {
|
|||
|
if (populationExcels.size() > MAX_READ_COUNT) {
|
|||
|
// 超过读取最大量,执行保存,
|
|||
|
handle(populationExcels);
|
|||
|
populationExcels.clear();
|
|||
|
} else {
|
|||
|
// 未达到读取最大量,继续读取
|
|||
|
populationExcels.add(populationExcel);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
|
|||
|
handle(populationExcels);
|
|||
|
populationExcels.clear();
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 处理
|
|||
|
*
|
|||
|
* @param rows
|
|||
|
*/
|
|||
|
public abstract void handle(List<PopulationExcel> rows);
|
|||
|
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
## 导入代码
|
|||
|
|
|||
|
### controller 层
|
|||
|
|
|||
|
```java
|
|||
|
@ApiOperation(value = "试题Excel", notes = "试题Excel接口")
|
|||
|
@ApiImplicitParams({
|
|||
|
@ApiImplicitParam(name = "excel", value = "文件名称", paramType = "form"),
|
|||
|
})
|
|||
|
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
|
|||
|
@PostMapping("import-excel")
|
|||
|
public UploadExcelResultDTO importExcel(MultipartFile excel) throws IOException {
|
|||
|
if (Objects.isNull(excel)) {
|
|||
|
throw new ParamsException("Excel不能为空");
|
|||
|
}
|
|||
|
// 判断后缀
|
|||
|
if (!excel.getOriginalFilename().endsWith(IFileConstant.EXCEL_SUFFIX_XLS)
|
|||
|
&& !excel.getOriginalFilename().endsWith(IFileConstant.EXCEL_SUFFIX_XLSX)) {
|
|||
|
throw new ParamsException("文件格式为Excel");
|
|||
|
}
|
|||
|
return populationService.importExcel(excel);
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### service 层
|
|||
|
|
|||
|
```java
|
|||
|
@Override
|
|||
|
public UploadExcelResultDTO importExcel(MultipartFile excel) throws IOException {
|
|||
|
|
|||
|
// 要输出的错误内容
|
|||
|
List<PopulationExcelError> populationExcelErrors = new ArrayList<>();
|
|||
|
|
|||
|
long startTime = System.currentTimeMillis();
|
|||
|
// 读取 Excel
|
|||
|
EasyExcel.read(excel.getInputStream(), PopulationExcel.class, new PopulationExcelListener() {
|
|||
|
@Override
|
|||
|
public void handle(List<PopulationExcel> populationExcels) {
|
|||
|
// 这里处理数据,一般是入库
|
|||
|
|
|||
|
}
|
|||
|
}).headRowNumber(2).sheet().doRead();
|
|||
|
long endTime = System.currentTimeMillis();
|
|||
|
|
|||
|
// 生成的错误文件ID,下载时使用
|
|||
|
String excelFileId = null;
|
|||
|
if (populationExcelErrors.size() > 0) {
|
|||
|
excelFileId = new AbstractErrorExcelHandler<PopulationExcelError>(fileService) {
|
|||
|
@Override
|
|||
|
public List<List<String>> excelHeaderNames() {
|
|||
|
// 构建错误 Excel 标题,与错误类对应
|
|||
|
return simpleExcelHeader(new String[]{"序号", "姓名", "身份证", "错误原因"});
|
|||
|
}
|
|||
|
}.saveErrorExcel(populationExcelErrors);
|
|||
|
}
|
|||
|
// 返回实体类,
|
|||
|
return new UploadExcelResultDTO(populationExcelErrors.size(), endTime - startTime, excelFileId);
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
## 模板下载接口
|
|||
|
```java
|
|||
|
@GetMapping("upload/upload-excel-template")
|
|||
|
public void excelTemplate(HttpServletResponse response) throws IOException {
|
|||
|
InputStream inputStream = PopulationRouteController.class.getClassLoader().getResourceAsStream("templates/population/upload/upload-excel-template.xls");
|
|||
|
RequestUtil.download(response, inputStream, "人口导入模板.xls");
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
## 列表导入按钮
|
|||
|
|
|||
|
```html
|
|||
|
<button type="button" class="layui-btn layui-btn-sm" id="uploadExcel">
|
|||
|
<i class="fa fa-lg fa-cloud-upload"></i> 导入数据
|
|||
|
</button>
|
|||
|
```
|
|||
|
|
|||
|
## 列表导入事件
|
|||
|
|
|||
|
```javascript
|
|||
|
$(document).on('click', '#uploadExcel', function() {
|
|||
|
top.dialog.open({
|
|||
|
url: top.restAjax.path('route/population/upload/upload-excel', []),
|
|||
|
title: '导入人口数据',
|
|||
|
width: '300px',
|
|||
|
height: '196px',
|
|||
|
onClose: function() {
|
|||
|
reloadTable();
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
## 导入页面代码
|
|||
|
|
|||
|
```html
|
|||
|
<!doctype html>
|
|||
|
<html xmlns:th="http://www.thymeleaf.org">
|
|||
|
<head>
|
|||
|
<base th:href="${#request.getContextPath() + '/'}">
|
|||
|
<meta charset="utf-8">
|
|||
|
<meta name="renderer" content="webkit">
|
|||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
|||
|
<link rel="stylesheet" href="assets/fonts/font-awesome/css/font-awesome.css"/>
|
|||
|
<link rel="stylesheet" href="assets/layuiadmin/layui/css/layui.css" media="all">
|
|||
|
<link rel="stylesheet" href="assets/layuiadmin/style/admin.css" media="all">
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<div class="layui-anim layui-anim-fadein">
|
|||
|
<div class="layui-card" style="text-align: center;">
|
|||
|
<div class="layui-card-body" style="padding: 15px;">
|
|||
|
<blockquote class="layui-elem-quote">下载“下载模板”整理数据,点击“导入数据”上传,格式为xls或xlsx</blockquote>
|
|||
|
<button id="downloadFile" type="button" class="layui-btn layui-btn" onclick="window.open('route/population/upload/upload-excel-template', 'downloadTarget')">
|
|||
|
<i class="fa fa-lg fa-cloud-download"></i> 下载模板
|
|||
|
</button>
|
|||
|
<button type="button" class="layui-btn layui-btn" id="uploadExcel">
|
|||
|
<i class="fa fa-lg fa-cloud-upload"></i> 导入数据
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<iframe style="display: none" name="downloadTarget"></iframe>
|
|||
|
</div>
|
|||
|
<script src="assets/layuiadmin/layui/layui.js"></script>
|
|||
|
<script>
|
|||
|
layui.config({
|
|||
|
base: 'assets/layuiadmin/' //静态资源所在路径
|
|||
|
}).extend({
|
|||
|
index: 'lib/index' //主入口模块
|
|||
|
}).use(['index', 'upload', 'common'], function(){
|
|||
|
var $ = layui.$;
|
|||
|
var form = layui.form;
|
|||
|
var common = layui.common;
|
|||
|
|
|||
|
function closeBox() {
|
|||
|
parent.layer.close(parent.layer.getFrameIndex(window.name));
|
|||
|
}
|
|||
|
|
|||
|
// 初始化Excel上传
|
|||
|
function initExcelUpload() {
|
|||
|
// Excel上传
|
|||
|
var uploadLoading;
|
|||
|
layui.upload.render({
|
|||
|
elem: '#uploadExcel',
|
|||
|
url: 'api/population/import-excel',
|
|||
|
accept: 'file',
|
|||
|
exts: 'xls|xlsx',
|
|||
|
field: 'excel',
|
|||
|
before: function() {
|
|||
|
uploadLoading = layer.msg('正在上传,请稍后...', {icon: 16, time: 0, shade: 0.3});
|
|||
|
},
|
|||
|
done: function(data) {
|
|||
|
layer.close(uploadLoading);
|
|||
|
if(data.failedCount > 0) {
|
|||
|
layer.open({
|
|||
|
type: 1,
|
|||
|
title: false,
|
|||
|
closeBtn: 0,
|
|||
|
shadeClose: true,
|
|||
|
skin: '',
|
|||
|
content: '<div style="padding: 15px;">' +
|
|||
|
'<div>失败数量:'+ data.failedCount +'</div><br/>' +
|
|||
|
'<div><a class="error-excel" href="javascript:void(0);" onclick="window.open(\'route/file/download/false/'+ data.errorExcel +'\');" target="downloadTarget">点击下载错误信息</a></div>' +
|
|||
|
'</div>'
|
|||
|
});
|
|||
|
} else {
|
|||
|
layer.msg('导入成功', {time: 2000}, function() {
|
|||
|
closeBox();
|
|||
|
});
|
|||
|
}
|
|||
|
},
|
|||
|
error: function(data, index){
|
|||
|
layer.close(uploadLoading);
|
|||
|
if(data != null) {
|
|||
|
top.dialog.msg(data.msg);
|
|||
|
}
|
|||
|
},
|
|||
|
});
|
|||
|
}
|
|||
|
initExcelUpload();
|
|||
|
|
|||
|
$('.close').on('click', function() {
|
|||
|
closeBox();
|
|||
|
});
|
|||
|
});
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
```
|