多sheet excel 导出

在处理大量数据导出到Excel时,将数据分到多个Sheet后统一导出是一种高效且常见的方法。下面用一个流程图帮你快速了解核心流程,然后再看具体的代码实现。

复制代码
flowchart TD
    A[开始导出请求] --> B[计算数据总量与Sheet数量]
    B --> C[创建ExcelWriter实例]
    C --> D[循环创建每个Sheet]
    D --> E{是否还有未处理数据?}
    E -- 是 --> F[分页查询当前Sheet数据]
    F --> G[创建WriteSheet并设置名称]
    G --> H[将数据写入当前Sheet]
    H --> D
    E -- 否 --> I[执行excelWriter.finish]
    I --> J[关闭输出流]
    J --> K[导出完成]

下面是基于 ​​EasyExcel​​ 库的具体实现方案和代码示例。

📝 核心代码实现

首先,在项目的 pom.xml 文件中添加 EasyExcel 依赖。

复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version> <!-- 请使用最新稳定版本 -->
</dependency>

下面是实现分 Sheet 导出的关键代码:

复制代码
@Service
public class DataExportService {

    @Autowired
    private YourMapper yourMapper; // 你的数据访问层

    /**
     * 将大数据量分Sheet导出为Excel
     * @param response HttpServletResponse
     */
    public void exportLargeDataToSheets(HttpServletResponse response) {
        // 1. 设置响应头,告诉浏览器这是一个Excel文件下载请求
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
        String fileName = "大数据导出示例.xlsx";
        try {
            fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName);

        // 2. 获取数据总量,并计算需要的Sheet数
        long totalCount = yourMapper.selectCount(null); // 假设的方法,获取总数据量
        int batchSize = 5000; // 每个Sheet存放的数据行数,可调整
        int sheetCount = (int) (totalCount / batchSize) + (totalCount % batchSize > 0 ? 1 : 0);

        // 3. 创建ExcelWriter实例
        ExcelWriter excelWriter = null;
        try (OutputStream outputStream = response.getOutputStream()) {
            excelWriter = EasyExcel.write(outputStream, YourDataModel.class).build(); // YourDataModel是你的数据模型类

            // 4. 循环创建并写入每个Sheet
            for (int sheetIndex = 0; sheetIndex < sheetCount; sheetIndex++) {
                // 计算当前页的起始位置
                long currentPage = sheetIndex + 1;
                long startRow = sheetIndex * batchSize;
                // 分页查询数据
                List<YourDataModel> dataList = yourMapper.selectBatch(startRow, batchSize); // 假设的分页查询方法

                // 创建WriteSheet,并指定Sheet名称(例如:Sheet1, Sheet2...)
                WriteSheet writeSheet = EasyExcel.writerSheet(sheetIndex, "数据页_" + (sheetIndex + 1)).build();

                // 将当前批次的数据写入到对应的Sheet中
                excelWriter.write(dataList, writeSheet);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5. 非常重要!最终必须调用finish方法,才会真正写出文件,并关闭相关资源。
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }
}

💡 代码要点解析

  • ​数据查询​ :关键在于​分页查询​ 。使用 limit startRow, batchSize 这样的SQL语句,分批从数据库拉取数据,避免一次性加载全部数据导致内存溢出(OOM)。你需要根据使用的持久层框架(如MyBatis)实现对应的分页查询方法。
  • ​Sheet管理​ :通过循环创建多个 WriteSheet 对象,每个对象对应Excel中的一个工作表。writerSheet(sheetIndex, "Sheet名称") 方法用于指定Sheet的索引和名称。
  • ​资源管理​ExcelWriterOutputStream 必须正确关闭。在 finally 块中调用 excelWriter.finish() 是确保资源释放和文件正确生成的关键。

🚀 进阶优化建议

当数据量特别大(例如百万行以上)时,可以考虑以下优化方案:

  • ​异步导出​:对于耗时很长的导出任务,建议采用异步处理。用户触发导出后,服务端生成一个任务ID并立即返回。任务在后台执行,用户可通过任务ID轮询进度或等待完成后下载。
  • ​多线程并行处理​:如果不同Sheet的数据相互独立,可以使用多线程并行查询和写入,充分利用多核CPU能力。但要注意线程安全和数据库连接池压力。
  • ​限制与压缩​:Excel单个Sheet最多支持约104万行数据。如果总数据量远超于此,分Sheet是必须的。最终生成的文件如果过大,可以考虑打包成ZIP格式提供下载。

⚠️ 注意事项

  • ​内存监控​ :即使分页查询,也要注意每批数据的大小(batchSize),避免单个批次数据过大。建议根据实际数据行的内存占用来调整 batchSize,通常设置在几千到一万条记录为宜。
  • ​事务与连接​ :长时间执行的导出任务可能会占用数据库连接。确保查询方法的事务传播行为设置正确(例如 @Transactional(readOnly = true, timeout = 60)),避免长时间占用写事务。

希望这份详细的指南和代码示例能帮助你顺利实现需求!如果你在具体实现中遇到其他问题,可以随时提出。

相关推荐
无毁的湖光Al6 分钟前
日常问题排查-Younggc突然变长
java·jvm·后端
wu_jing_sheng010 分钟前
Python中使用HTTP 206状态码实现大文件下载的完整指南
开发语言·前端·python
_星辰大海乀10 分钟前
网络原理 -- HTTP
java·服务器·http·get方法·post方法
没有bug.的程序员26 分钟前
电商系统分布式架构实战:从单体到微服务的演进之路
java·分布式·微服务·云原生·架构·监控体系·指标采集
励志不掉头发的内向程序员29 分钟前
【Linux系列】掌控 Linux 的脉搏:深入理解进程控制
linux·运维·服务器·开发语言·学习
Query*35 分钟前
Java 设计模式——代理模式:从静态代理到 Spring AOP 最优实现
java·设计模式·代理模式
梵得儿SHI36 分钟前
Java 反射机制深度解析:从对象创建到私有成员操作
java·开发语言·class对象·java反射机制·操作类成员·三大典型·反射的核心api
沐知全栈开发39 分钟前
Foundation 折叠列表
开发语言
JAVA学习通40 分钟前
Spring AI 核心概念
java·人工智能·spring·springai
望获linux42 分钟前
【实时Linux实战系列】实时 Linux 在边缘计算网关中的应用
java·linux·服务器·前端·数据库·操作系统