【Spring Boot实战】EasyPoi神技:一篇搞定“一对多+图片”导入!

简介: 本文分享使用Spring Boot + EasyPoi实现复杂Excel导入(含图片和一对多数据)的实战经验。涵盖环境配置、实体注解、一对多处理、图片上传及大文件监听器优化,解决内存溢出问题。附替代官方文档链接,助力高效开发。

最近做了一个excel 导入相关的功能,因为导入的excel 格式比较复杂,并且还有图片,做的时候也是费了一点功夫,因此整理了这篇文章

推荐大家看官方文档,但是我在访问官方文档时报了权限不足
0a02888a78ab4aaf8f9eb13517efc79a_MD5.jpeg
搜了一下好像是说截至2024年11月18日已经无法打开了,但我又找到了个优化版在语雀上,这里分享给大家
EasyPoi官方文档优化版: https://www.yuque.com/guomingde/easypoi

读前须知

本文采用的是Spring boot + EasyPoi ,同时使用了lombak 来简化实体类配置。使用Spring 或者其他框架,对应依赖需要修改,代码部分是一样的

EasyPoi 是什么?

简单说,EasyPoi 是一个基于 Apache POI 的封装库,它的核心哲学就是:让Excel导入导出变得跟呼吸一样简单!

核心优势:

  • 注解驱动:一个注解搞定一个字段,告别繁琐的API。
  • 功能强大:轻松处理一对多、图片、公式、下拉框等。
  • 代码极简:比原生POI代码量减少90%!

使用步骤

导入表格示例:
68fb64ac7795d3032771b9ddb4208437_MD5.jpeg

第一步:Maven依赖

springboot整合EasyPoi

<!-- EasyPoi 核心依赖 -->
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-spring-boot-starter</artifactId>
    <version>4.5.0</version>
</dependency>

<dependency>  
    <groupId>org.projectlombok</groupId>  
    <artifactId>lombok</artifactId>  
    <optional>true</optional>  
</dependency>

spring 下依赖包配置

<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-base</artifactId>
    <version>4.1.0</version>
</dependency>
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-web</artifactId>
    <version>4.1.0</version>
</dependency>
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-annotation</artifactId>
    <version>4.1.0</version>
</dependency>

第二步:创建实体类

这是最关键的一步!我们用注解来告诉EasyPoi数据结构。

1. 创建主实体(订单)

import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
import java.util.List;

@Data
public class OrderDTO {
   

    @Excel(name = "订单编号", width = 20)
    private String orderNo;

    @Excel(name = "客户名称")
    private String customerName;

    // 👇 核心注解!处理一对多关系
    @ExcelCollection(name = "订单商品") // 这里的name在导入时没啥用,但导出时是表头
    private List<OrderItemDTO> items;

}

划重点:

  • @ExcelCollection: 就是处理一对多的关键配置! 它会自动将Excel中连续的多行数据,根据主实体的字段(如订单编号)进行分组,封装成一个List。

2. 创建子实体(商品项)

import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelTarget;

@Data
public class OrderItemDTO {
   

    @Excel(name = "商品名称") // 👈 对应Excel列名
    private String productName;

    @Excel(name = "商品数量")
    private Integer quantity;

    // 👇 核心注解!处理图片导入
    @Excel(name = "商品图片", type = 2, savePath = "uploads/images/")
    private String imagePath; // 字段存储的是图片保存后的路径
}

划重点:

  • @ExcelTarget: 标记这是一个Excel实体。
  • @Excel(name = "..."): 将字段与Excel列名绑定。
  • @Excel(type = 2, savePath = "..."): type = 2 表示这是一个图片类型。savePath 极其重要,它指定了图片上传后保存的服务器路径。

遇到的坑:
在导入图片时,一开始我在 Excel 中使用的是嵌入模式,在导入时一直读不到图片,后来改成浮动式,才能正常读到文件。官网上好像也没有特别说明,如果你也遇到这个问题,可以试着改成浮动试一下。

第三步:编写Controller

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;

@RestController
public class ExcelImportController {
   

    @PostMapping("/import/order")
    public String importOrder(MultipartFile file) throws Exception {
   

        // 1. 设置导入参数
        ImportParams params = new ImportParams();
        params.setTitleRows(0); // 标题行,从第0行开始
        params.setHeadRows(1); // 表头行,从第1行开始
        params.setDataRows(0); // 数据行,从第0行开始(相对于表头)

        // 👇 关键!开启一对多导入模式
        params.setNeedVerify(true); // 开启验证
        params.setGroupFieldIndex(0); // 👈 指定分组依据的列索引(订单编号在第0列)

        // 2. 执行导入
        // 注意:这里导入的是主实体 OrderDTO
        List<OrderDTO> orderList = ExcelImportUtil.importExcel(file.getInputStream(), OrderDTO.class, params);

        // 3. 处理结果
        for (OrderDTO order : orderList) {
   
            System.out.println("订单编号: " + order.getOrderNo());
            System.out.println("客户名称: " + order.getCustomerName());
            for (OrderItemDTO item : order.getItems()) {
   
                System.out.println("  - 商品: " + item.getProductName() + ", 数量: " + item.getQuantity() + ", 图片路径: " + item.getImagePath());
            }
            // ... 在这里进行你的数据库保存操作 ...
        }

        return "导入成功!共导入 " + orderList.size() + " 个订单。";
    }
}

代码解析:

  1. ImportParams 是导入的配置中心,可以设置标题、表头位置等。
  2. params.setGroupFieldIndex(0); 这是处理一对多的灵魂! 它告诉EasyPoi:“请看第0列(订单编号),如果这列的值不变,就认为是同一个订单,把后面的数据塞到它的 items 列表里!”
  3. ExcelImportUtil.importExcel(...) 一行代码,搞定所有解析和封装!图片会自动保存到你在DTO里设置的 savePath,路径赋值给 imagePath 字段。

大量数据导入

如果导入的数据量很大的话,直接使用上面的代码,很可能导入后直接内存溢出!这时候就可以使用监听器模式进行优化

创建自定义监听器

/**
 *  订单导入监听器
 * 核心:实现 IExcelReadListener 接口
 * 作用:流式处理每一行数据
 */
@Component
@Slf4j
public class OrderImportListener implements IExcelReadListener<OrderDTO> {
   

    @Autowired
    private OrderService orderService;

    private List<OrderDTO> orderCache = new ArrayList<>();
    private int batchSize = 1000; // 🎯 批处理大小
    private int totalCount = 0;

    /**
     * 核心方法:处理每一行数据
     */
    @Override
    public void invoke(OrderDTO order, AnalysisContext context) {
   
        totalCount++;

        //  数据校验
        if (isValidOrder(order)) {
   
            orderCache.add(order);

            //  批量保存
            if (orderCache.size() >= batchSize) {
   
                saveBatch();
                orderCache.clear();
            }
        } else {
   
            log.warn("数据校验失败,行号: {}", context.getCurrentRowNum());
        }

        //  进度反馈(每1000行日志一次)
        if (totalCount % 1000 == 0) {
   
            log.info(" 已处理: {} 行数据", totalCount);
        }
    }

    /**
     *  所有数据解析完成
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
   
        // 处理最后一批数据
        if (!orderCache.isEmpty()) {
   
            saveBatch();
        }
        log.info("🎉 导入完成!总计处理: {} 行", totalCount);
    }

    /**
     * 🎯批量保存到数据库
     */
    private void saveBatch() {
   
        try {
   
            // 这里可以处理图片和一对多关系
            orderService.batchSaveOrders(orderCache);
            log.info("✅ 批量保存成功,数量: {}", orderCache.size());
        } catch (Exception e) {
   
            log.error("❌ 批量保存失败", e);
            throw new RuntimeException("批量保存异常");
        }
    }

    /**
     *  数据校验
     */
    private boolean isValidOrder(OrderDTO order) {
   
        return order != null && 
               StringUtils.isNotBlank(order.getOrderNo()) &&
               order.getItems() != null && !order.getItems().isEmpty();
    }
}

Controller层调用

调用的修改也很简单,调用导入方式时,传入监听器即可

@RestController
@RequestMapping("/api/order")
@Slf4j
public class OrderImportController {
   

    @Autowired
    private OrderImportListener orderImportListener;

    /**
     *  大文件导入接口
     */
    @PostMapping("/import-large")
    public ResponseEntity<?> importLargeOrder(@RequestParam("file") MultipartFile file) {
   

        try {
   
            //  创建导入参数
            ImportParams params = new ImportParams();
            params.setTitleRows(0);
            params.setHeadRows(1);

            //  关键配置:使用监听器模式
            ExcelImportUtil.importExcel(
                file.getInputStream(), 
                OrderDTO.class, 
                params, 
                orderImportListener  // 👈 传入监听器
            );

            return ResponseEntity.ok("大文件导入已启动,请查看日志获取进度");

        } catch (Exception e) {
   
            log.error("大文件导入失败", e);
            return ResponseEntity.badRequest().body("导入失败: " + e.getMessage());
        }
    }
}

总结

EasyPoi让复杂Excel导入变得如此简单:

  • ✅ 一对多关系:@ExcelCollection + 分组配置
  • ✅ 图片导入:type=2 + savePath
  • ✅ 代码简洁:注解驱动,告别繁琐API
  • ✅ 大量数据时使用监听器

从此告别加班,准时下班不是梦!

相关文章
|
2天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
|
4天前
|
云安全 数据采集 人工智能
古茗联名引爆全网,阿里云三层防护助力对抗黑产
阿里云三层校验+风险识别,为古茗每一杯奶茶保驾护航!
古茗联名引爆全网,阿里云三层防护助力对抗黑产
|
4天前
|
存储 机器学习/深度学习 人工智能
大模型微调技术:LoRA原理与实践
本文深入解析大语言模型微调中的关键技术——低秩自适应(LoRA)。通过分析全参数微调的计算瓶颈,详细阐述LoRA的数学原理、实现机制和优势特点。文章包含完整的PyTorch实现代码、性能对比实验以及实际应用场景,为开发者提供高效微调大模型的实践指南。
534 2
kde
|
4天前
|
人工智能 关系型数据库 PostgreSQL
n8n Docker 部署手册
n8n是一款开源工作流自动化平台,支持低代码与可编程模式,集成400+服务节点,原生支持AI与API连接,可自托管部署,助力团队构建安全高效的自动化流程。
kde
362 3
|
2天前
|
Linux 虚拟化 iOS开发
VMware Workstation Pro 25H2 for Windows & Linux - 领先的免费桌面虚拟化软件
VMware Workstation Pro 25H2 for Windows & Linux - 领先的免费桌面虚拟化软件
754 4
VMware Workstation Pro 25H2 for Windows & Linux - 领先的免费桌面虚拟化软件
|
3天前
|
JavaScript 开发工具 Android开发
如何在原生 App 中调用 Uniapp 的页面?
如何在原生 App 中调用 Uniapp 的页面?
243 138
|
4天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段四:学术分析 AI 项目 RAG 落地指南:基于 Spring AI 的本地与阿里云知识库实践
本文介绍RAG(检索增强生成)技术,结合Spring AI与本地及云知识库实现学术分析AI应用,利用阿里云Qwen-Plus模型提升回答准确性与可信度。
254 91
AI 超级智能体全栈项目阶段四:学术分析 AI 项目 RAG 落地指南:基于 Spring AI 的本地与阿里云知识库实践