前言
在实际的业务开发中,我们经常需要处理Excel文件的导入导出功能。EasyExcel作为阿里巴巴开源的一款Excel处理工具,以其高性能、低内存占用而广受欢迎。今天我们来探讨如何使用SpringBoot整合EasyExcel,并实现表头校验功能。
一、什么是EasyExcel
EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
二、项目依赖
首先在pom.xml中添加必要的依赖:
XML
<dependencies>
<spring-boot-starter-web>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</spring-boot-starter-web>
<easyexcel>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</easyexcel>
<lombok>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</lombok>
</dependencies>
三、实现表头校验
1. 创建Excel数据模型
java
@Data
public class UserImportVO {
@ExcelProperty("姓名")
private String name;
@ExcelProperty("年龄")
private Integer age;
@ExcelProperty("邮箱")
private String email;
@ExcelProperty("手机号")
private String phone;
}
2. 自定义表头校验器
java
public class HeadValidator implements ReadListener<UserImportVO> {
private final List<String> expectedHead;
private final List<String> actualHead = new ArrayList<>();
private boolean headValid = true;
private String errorMsg;
public HeadValidator() {
// 预期的表头列表
this.expectedHead = Arrays.asList("姓名", "年龄", "邮箱", "手机号");
}
@Override
public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
// 提取实际表头
headMap.values().forEach(cellData -> {
String cellValue = cellData.getStringValue();
actualHead.add(cellValue);
});
// 校验表头
validateHead();
}
private void validateHead() {
if (actualHead.size() != expectedHead.size()) {
headValid = false;
errorMsg = String.format("表头数量不匹配,期望%d个,实际%d个",
expectedHead.size(), actualHead.size());
return;
}
for (int i = 0; i < expectedHead.size(); i++) {
if (!expectedHead.get(i).equals(actualHead.get(i))) {
headValid = false;
errorMsg = String.format("第%d列表头不匹配,期望:%s,实际:%s",
i + 1, expectedHead.get(i), actualHead.get(i));
break;
}
}
}
@Override
public void invoke(UserImportVO data, AnalysisContext context) {
// 表头校验不通过时,直接抛出异常
if (!headValid) {
throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(),
null, errorMsg, null);
}
// 数据处理逻辑
processData(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 所有数据解析完成后的操作
System.out.println("Excel解析完成");
}
private void processData(UserImportVO data) {
// 具体的业务处理逻辑
System.out.println("处理数据:" + data);
}
public boolean isHeadValid() {
return headValid;
}
public String getErrorMsg() {
return errorMsg;
}
}
3. 更灵活的表头校验器(支持顺序不敏感)
java
public class FlexibleHeadValidator implements ReadListener<UserImportVO> {
private final Set<String> expectedHeadSet;
private final Set<String> actualHeadSet = new HashSet<>();
private boolean headValid = true;
private String errorMsg;
public FlexibleHeadValidator() {
// 预期的表头集合(顺序不敏感)
this.expectedHeadSet = new HashSet<>(Arrays.asList("姓名", "年龄", "邮箱", "手机号"));
}
@Override
public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
// 提取实际表头
headMap.values().forEach(cellData -> {
String cellValue = cellData.getStringValue();
actualHeadSet.add(cellValue);
});
// 校验表头
validateHead();
}
private void validateHead() {
if (actualHeadSet.size() != expectedHeadSet.size()) {
headValid = false;
errorMsg = String.format("表头数量不匹配,期望%d个,实际%d个",
expectedHeadSet.size(), actualHeadSet.size());
return;
}
if (!actualHeadSet.containsAll(expectedHeadSet)) {
headValid = false;
Set<String> missingHeaders = new HashSet<>(expectedHeadSet);
missingHeaders.removeAll(actualHeadSet);
errorMsg = "缺少必要的表头:" + String.join(",", missingHeaders);
}
}
// 其他方法同上...
}
4. 控制器层实现
java
@RestController
@RequestMapping("/excel")
public class ExcelImportController {
@PostMapping("/import")
public ResponseEntity<String> importExcel(@RequestParam("file") MultipartFile file) {
try {
HeadValidator headValidator = new HeadValidator();
EasyExcel.read(file.getInputStream(), UserImportVO.class, headValidator)
.sheet()
.headRowNumber(1)
.doRead();
if (!headValidator.isHeadValid()) {
return ResponseEntity.badRequest().body("表头校验失败:" + headValidator.getErrorMsg());
}
return ResponseEntity.ok("Excel导入成功");
} catch (ExcelDataConvertException e) {
return ResponseEntity.badRequest().body("数据解析错误,第" + (e.getRowIndex() + 1) + "行:" + e.getMessage());
} catch (Exception e) {
return ResponseEntity.badRequest().body("文件处理失败:" + e.getMessage());
}
}
}
5. 全局异常处理
java
java
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MultipartException.class)
public ResponseEntity<String> handleMultipartException(MultipartException e) {
return ResponseEntity.badRequest().body("文件上传失败:" + e.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception e) {
return ResponseEntity.badRequest().body("系统错误:" + e.getMessage());
}
}