1、业务需求接口文档
- GET
/sys/person/exportExcel:导出居民列表,返回生成文件名; - POST
/sys/person/excelUpload:上传 excel 文件 MultipartFile,保存临时文件返回文件名; - POST
/sys/person/parsefile:传入上传后的文件名,读取临时 excel,解析每一行居民数据,校验小区是否存在;返回成功或者失败,提示第几行错误。
返回示例:
//导入成功
{"msg":"操作成功","code":200,"data":"数据导入成功!","status":"success"}
//导入失败
{"msg":"操作成功","code":200,"data":"第3行小区名称不存在!","status":"fail"}
2、pom 依赖
<!-- poi excel -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
3、接口流程
- 前端选择 Excel 文件,调用
excelUpload上传,后端保存到服务器临时目录,返回文件名字符串; - 前端拿到文件名,调用
parsefile接口传入文件名; - 后端读取临时 Excel 文件,逐行解析居民;
- 每行做业务校验(小区名称是否存在、手机号格式);
- 校验全部通过批量 insert 数据库;如果某一行校验失败,直接返回提示:
第X行小区名称不存在!。
4、文件上传接口 excelUpload
@PostMapping("/excelUpload")
public Result excelUpload(@RequestParam("file") MultipartFile file){
//生成唯一uuid文件名,保留后缀
String suffix = FilenameUtils.getExtension(file.getOriginalFilename());
String fileName = UUID.randomUUID().toString().replace("-","")+"."+suffix;
//临时目录
File tempDir = new File("./tempExcel/");
if(!tempDir.exists()) tempDir.mkdirs();
File saveFile = new File(tempDir,fileName);
file.transferTo(saveFile);
return Result.success(fileName);
}
5、解析 Excel 工具方法
/**
* 读取excel,返回居民list,返回错误行号信息
*/
public ResultDTO<List<PersonImportDTO>> readExcel(String fileName){
File file = new File("./tempExcel/"+fileName);
Workbook workbook = WorkbookFactory.create(file);
Sheet sheet = workbook.getSheetAt(0);
List<PersonImportDTO> list = new ArrayList<>();
//第0行为表头,从第1行开始读数据
for(int i=1;i<=sheet.getLastRowNum();i++){
Row row = sheet.getRow(i);
if(row == null) continue;
PersonImportDTO dto = new PersonImportDTO();
dto.setCommunityName(row.getCell(0).getStringCellValue());
dto.setUserName(row.getCell(1).getStringCellValue());
dto.setMobile(row.getCell(2).getStringCellValue());
//业务校验小区是否存在
Community community = communityMapper.selectByName(dto.getCommunityName());
if(community == null){
return ResultDTO.fail("第"+(i+1)+"行小区名称不存在!");
}
dto.setCommunityId(community.getCommunityId());
list.add(dto);
}
workbook.close();
return ResultDTO.success(list);
}
6、parsefile 解析文件接口
@PostMapping("/parsefile")
public Result parseFile(@RequestBody String fileName){
ResultDTO<List<PersonImportDTO>> readResult = excelUtil.readExcel(fileName);
if(!readResult.isSuccess()){
return Result.fail(readResult.getMsg(),"fail");
}
List<PersonImportDTO> dataList = readResult.getData();
//批量插入数据库
personService.batchSave(dataList);
//可选:解析完成删除临时excel文件
return Result.success("数据导入成功!","success");
}
7、导出接口 /exportExcel
查询居民分页列表,写入 Excel,保存文件返回文件名。
8、踩坑总结
- HSSFWorkbook(xls)、XSSFWorkbook(xlsx),推荐使用
WorkbookFactory.create(file)自动适配; - 大文件(上万行)使用 SXSSFWorkbook,避免 OOM 内存溢出;
- MultipartFile 大小限制,修改 yml spring.servlet.multipart 配置;
- 临时文件需要定时清理,防止磁盘占满;
- cell 获取数据,数字单元格 getStringCellValue () 会抛异常,需要统一工具转换单元格值;
- 导入事务:只要任意一行校验失败,整体不插入数据库;全部校验通过再批量保存。