SpringBoot+EasyPOI实现百万级数据导出Excel实战指南

 更新时间:2025年09月18日 09:44:30   作者:寒冰碧海  
这篇文章主要为大家详细介绍了一款基于 EasyPOI 和 Apache POI SXSSF 的批量Excel导出工具类 BatchExcelExporter,支持大数据量分页处理,下面小编就来和大家详细介绍一下吧

工具简介

本文介绍一款基于 EasyPOIApache POI SXSSF 的批量Excel导出工具类 BatchExcelExporter,支持大数据量分页处理,避免内存溢出(OOM),适合百万级数据导出场景。

环境准备

1. 引入Maven依赖

pom.xml 中添加以下依赖(确保使用最新版本):

<!-- EasyPOI 核心库 -->
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-spring-boot-starter</artifactId>
    <version>4.4.0</version>
</dependency>

<!-- Apache POI (SXSSF 需要) -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.3</version>
</dependency>

2. 引入工具类

import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

public class BatchExcelExporter<T> {

    private final SXSSFWorkbook workbook;
    private final Class<T> clazz;
    private final List<ExcelExportEntity> exportEntities = new ArrayList<>();
    private int currentRowIndex = 0;

    public BatchExcelExporter(Class<T> clazz) {
        this.clazz = clazz;
        this.workbook = new SXSSFWorkbook(100); // 缓存100行到磁盘
        initHeader();
    }

    // 初始化表头(仅执行一次)
    private void initHeader() {
        // 解析实体类注解
        List<Field> fields = Arrays.stream(clazz.getDeclaredFields())
                .filter(f -> f.isAnnotationPresent(Excel.class))
                .sorted(Comparator.comparing(f -> {
                    Excel excel = f.getAnnotation(Excel.class);
                    return excel.orderNum();
                })).collect(Collectors.toList());

        // 构建导出结构
        fields.forEach(f -> {
            Excel excel = f.getAnnotation(Excel.class);
            exportEntities.add(new ExcelExportEntity(excel.name(), f.getName(), 4000));
        });

        // 创建表头
        workbook.createSheet("数据").createRow(currentRowIndex++);
        addHeaderRow(workbook.getSheetAt(0).getRow(0));
    }

    // 添加表头行
    private void addHeaderRow(org.apache.poi.ss.usermodel.Row headerRow) {
        for (int i = 0; i < exportEntities.size(); i++) {
            headerRow.createCell(i).setCellValue(exportEntities.get(i).getName());
        }
    }

    // 批量添加数据(分页调用)
    public void addBatchData(List<T> batchData) throws Exception {
        org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
        for (T data : batchData) {
            org.apache.poi.ss.usermodel.Row row = sheet.createRow(currentRowIndex++);
            fillDataRow(row, data);
        }
        // 每500行刷新到磁盘(根据数据量调整)
        if (currentRowIndex % 500 == 0) {
            ((SXSSFSheet) sheet).flushRows(500);
        }
    }

    // 填充数据行(利用反射动态取值)
    private void fillDataRow(org.apache.poi.ss.usermodel.Row row, T data) throws Exception {
        for (int i = 0; i < exportEntities.size(); i++) {
            Field field = clazz.getDeclaredField((String) exportEntities.get(i).getKey());
            field.setAccessible(true);
            Object value = field.get(data);
            row.createCell(i).setCellValue(value != null ? value.toString() : "");
        }
    }

    // 最终写入文件
    public void exportToFile(String filePath) throws Exception {
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            workbook.write(fos);
        } finally {
            workbook.dispose(); // 清理临时文件
        }
    }
}

工具类核心功能

动态表头生成:通过解析实体类的 @Excel 注解自动生成表头。

分页批量写入:支持分批次添加数据,减少内存占用。

高性能处理:基于 SXSSFWorkbook,默认缓存100行到磁盘。

使用示例

1. 定义实体类

public class User {
    @Excel(name = "姓名", orderNum = "0")
    private String name;

    @Excel(name = "年龄", orderNum = "1")
    private Integer age;

    @Excel(name = "邮箱", orderNum = "2")
    private String email;

    // 省略Getter/Setter
}

2. 调用工具类导出数据

public class ExcelExportDemo {
    public static void main(String[] args) throws Exception {
        // 初始化导出器(指定实体类)
        BatchExcelExporter<User> exporter = new BatchExcelExporter<>(User.class);

        // 模拟分页查询数据(假设每次查询1000条)
        for (int page = 1; page <= 10; page++) {
            List<User> batchData = queryUsersByPage(page, 1000);
            exporter.addBatchData(batchData);
        }

        // 导出到文件
        exporter.exportToFile("user_export.xlsx");
    }

    private static List<User> queryUsersByPage(int page, int size) {
        // 模拟数据库查询(此处返回测试数据)
        List<User> users = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            users.add(new User("User" + i, 20 + i, "user" + i + "@example.com"));
        }
        return users;
    }
}

关键代码解析

1.表头自动生成

通过反射解析实体类的 @Excel 注解,按 orderNum 排序后生成表头:

List<Field> fields = Arrays.stream(clazz.getDeclaredFields())
        .filter(f -> f.isAnnotationPresent(Excel.class))
        .sorted(Comparator.comparing(f -> f.getAnnotation(Excel.class).orderNum()))
        .collect(Collectors.toList());

2.数据分页写入

批量添加数据时,每500行刷新到磁盘(避免内存堆积):

public void addBatchData(List<T> batchData) throws Exception {
    // ... 填充数据 ...
    if (currentRowIndex % 500 == 0) {
        ((SXSSFSheet) sheet).flushRows(500);  // 🚨 关键性能优化点!
    }
}

注意事项

字段类型匹配:确保实体类字段类型与Excel数据兼容(如日期格式需特殊处理)。

性能调优:根据服务器内存调整 SXSSFWorkbook 的缓存行数(默认100)。

异常处理:建议添加 try-catch 块捕获反射和IO异常。

效果展示

导出后的Excel文件示例:

姓名年龄邮箱
User020user0@example.com
User121user1@example.com

扩展优化方向

  • 样式自定义:通过 SXSSFCellStyle 设置单元格样式(如背景色、边框)。
  • 多Sheet支持:扩展工具类以支持多Sheet动态切换。
  • 异步导出:结合线程池实现异步导出任务。

到此这篇关于SpringBoot+EasyPOI实现百万级数据导出Excel实战指南的文章就介绍到这了,更多相关SpringBoot Excel数据导出内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java IO流操作(PipeInputStream、SequenceInputStream、BufferedInputStream)

    Java IO流操作(PipeInputStream、SequenceInputStream、Buffered

    管道流主要用于线程间通信,分为管道输入流(PipeInputStream)和管道输出流(PipeOutputStream),本文介绍了如何通过管道流进行数据发送和接收,具有一定的参考价值,感兴趣的可以了解一下
    2024-10-10
  • Spring Boot2发布调用REST服务实现方法

    Spring Boot2发布调用REST服务实现方法

    这篇文章主要介绍了Spring Boot2发布调用REST服务实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • SpringBoot集成Redis实现消息队列的方法

    SpringBoot集成Redis实现消息队列的方法

    这篇文章主要介绍了SpringBoot集成Redis实现消息队列的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • Java如何实现保证线程安全

    Java如何实现保证线程安全

    这篇文章主要介绍了Java如何实现保证线程安全问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • 解析Jmeter脱离Jenkins后Ant集成邮件通知问题

    解析Jmeter脱离Jenkins后Ant集成邮件通知问题

    今天来讲下本地的ant构建并发送邮件。配置下来挺顺利也挺简单的,对Jmeter脱离Jenkins后Ant集成邮件通知问题感兴趣的朋友跟随小编一起看看吧
    2021-12-12
  • springboot如何读取配置文件到静态工具类

    springboot如何读取配置文件到静态工具类

    这篇文章主要介绍了springboot实现读取配置文件到静态工具类方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 解决@NonNull @org.jetbrains.annotations.NotNull报红的问题

    解决@NonNull @org.jetbrains.annotations.NotNull报红的问题

    这篇文章主要介绍了解决@NonNull @org.jetbrains.annotations.NotNull报红的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • mybatis的association传递参数问题示例

    mybatis的association传递参数问题示例

    这篇文章主要介绍了mybatis的association传递参数问题,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-12-12
  • Spring入门基础之依赖注入

    Spring入门基础之依赖注入

    Idea中使用@Autowire注解会出现提示黄线,强迫症患者看着很难受,使用构造器注入或者setter方法注入后可解决,下面我们一起来看看
    2022-07-07
  • IDEA中添加xml配置文件时,显示file问题

    IDEA中添加xml配置文件时,显示file问题

    这篇文章主要介绍了IDEA中添加xml配置文件时,显示file问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12

最新评论