Apache POI 实现 Excel 上传解析、导出

1、业务需求接口文档

  1. GET /sys/person/exportExcel:导出居民列表,返回生成文件名;
  2. POST /sys/person/excelUpload:上传 excel 文件 MultipartFile,保存临时文件返回文件名;
  3. 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、接口流程

  1. 前端选择 Excel 文件,调用excelUpload上传,后端保存到服务器临时目录,返回文件名字符串;
  2. 前端拿到文件名,调用parsefile接口传入文件名;
  3. 后端读取临时 Excel 文件,逐行解析居民;
  4. 每行做业务校验(小区名称是否存在、手机号格式);
  5. 校验全部通过批量 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、踩坑总结

  1. HSSFWorkbook(xls)、XSSFWorkbook(xlsx),推荐使用WorkbookFactory.create(file)自动适配;
  2. 大文件(上万行)使用 SXSSFWorkbook,避免 OOM 内存溢出;
  3. MultipartFile 大小限制,修改 yml spring.servlet.multipart 配置;
  4. 临时文件需要定时清理,防止磁盘占满;
  5. cell 获取数据,数字单元格 getStringCellValue () 会抛异常,需要统一工具转换单元格值;
  6. 导入事务:只要任意一行校验失败,整体不插入数据库;全部校验通过再批量保存。
相关推荐
代立冬2 天前
927 个 PR:Apache SeaTunnel 3.0.0 正式发布!AI 时代,数据集成正在迎来新的可能
人工智能·apache
zhongben_keji3 天前
家政小程序开发全解:经营模式、品类矩阵、派单约束与信用核验
线性代数·矩阵·apache
weixin_177297220694 天前
二手旧物回收小程序先要解决的不是下单,是“上门之后价格变了怎么办“
小程序·apache
努力变的更好4 天前
H5与小程序之间的回调
前端·小程序·apache
十碗饭吃不饱6 天前
Apache POI 踩坑:setFontFamily 二次设置字体不生效、样式残留问题深度解析
java·word·apache
心随_风动7 天前
Ubuntu系统安装Apache服务教程
linux·ubuntu·apache
ly76898 天前
Kafka 消费者重试与死信:Spring for Apache Kafka 的边界设计
spring·kafka·apache·并发消费·消费者重试·死信主题·位移提交
HoneyMoose9 天前
Ubuntu Apache 配置多站点提示 SSL 错误
ubuntu·apache·ssl
骑着蜗牛撵大象3279 天前
一套引擎撑起树表双模型:Apache IoTDB 2.0 与 TsFile 的选型答卷
apache·时序数据库·iotdb·tsfile·树模型·表模型
凤山老林9 天前
Spring Boot 集成 Apache CXF 发布与消费 SOAP WebService:WSDL、WS-Security
spring boot·后端·apache·webservice·soap·apache cxf