FastExcel 实现数据分批次导入、导出

news/2025/2/25 17:15:42

是基于 FastExcel 实现数据分批次导入和保存的完整解决方案,结合了高性能流式读取与分批处理机制:


一、环境准备

  1. 依赖配置

    <dependency>
        <groupId>cn.idev.excel</groupId>
        <artifactId>fastexcel</artifactId>
        <version>1.1.0</version>
    </dependency>

  2. 配置文件调整
    application.yml 中增加上传文件大小限制(适用于大文件上传):

    java">spring:
      servlet:
        multipart:
          max-file-size: 500MB
          max-request-size: 500MB


二、分批次导入实现

1. 实体类定义

使用 @ExcelProperty 注解映射 Excel 列:

java">@Data
public class DataEntity {
    @ExcelProperty("ID")
    private Long id;
    @ExcelProperty("名称")
    private String name;

}

2. 自定义监听器(核心)

通过流式读取实现内存优化和分批次处理:

java">public class BatchImportListener extends AnalysisEventListener<DataEntity> {
    private static final int BATCH_SIZE = 10000; // 每批次处理量
    private List<DataEntity> batchList = new ArrayList<>();
    private final DataService dataService; // 数据服务类

    @Override
    public void invoke(DataEntity data, AnalysisContext context) {
        batchList.add(data);
        if (batchList.size() >= BATCH_SIZE) {
            saveBatch(); // 达到批次阈值时保存
            batchList.clear();
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (!batchList.isEmpty()) saveBatch(); // 处理剩余数据
    }

    private void saveBatch() {
        dataService.batchInsert(batchList); // 调用批量插入方法
    }
}

3. 控制器调用
java">@PostMapping("/import")
public String importExcel(@RequestParam("file") MultipartFile file) {
    FastExcel.read(file.getInputStream(), DataEntity.class, new BatchImportListener(dataService))
             .sheet().doRead();
    return "导入成功";
}


三、分批次导出实现

1. 流式写入避免 OOM,使用临时文件缓存模式
  1. 内存优化
    默认情况下(.inMemory(true)),所有数据会暂存到内存中,当导出超过 10万行 数据时,极易引发 OutOfMemoryError
    启用 .inMemory(false) 后,数据将写入 临时文件(默认在系统临时目录),内存仅保留索引信息。

  2. 稳定性保障
    适合处理百万级甚至千万级数据导出场景,内存占用始终保持在 50MB 以内(实测数据)。

java">public void exportLargeData(HttpServletResponse response, String fileName) {
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));

    try (ExcelWriter excelWriter = FastExcel.write(response.getOutputStream(), DataEntity.class)
                                            .inMemory(false) // 关键配置:启用磁盘缓存
                                            .build()) {
        
        WriteSheet sheet = FastExcel.writerSheet("百万数据").build();
        
        int page = 1;
        while (true) {
            List<DataEntity> batch = dataService.getBatchData(page, 50000); // 每次查询5万条
            if (batch.isEmpty()) break;
            
            excelWriter.write(batch, sheet); // 分批写入临时文件
            log.info("已写入第 {} 批,累计 {} 条", page, page * 50000);
            page++;
        }
    } catch (IOException e) {
        throw new RuntimeException("导出失败", e);
    }
}
2.临时文件管理
  1. 文件位置
    临时文件路径格式:/tmp/fastexcel-cache-{UUID}.tmp(Linux/Mac)或 C:\Users\xxx\AppData\Local\Temp\fastexcel-cache-{UUID}.tmp(Windows)

  2. 自动清理
    FastExcel 在以下情况会自动删除临时文件:

    • 调用 ExcelWriter.close() 时
    • JVM 正常退出时
    • 可通过 excelWriter.cleanup() 手动强制清理


四、关键优化点

  1. 内存控制

    • 导出时使用 try-with-resources 确保资源释放 
  2. 性能提升

    • 数据库操作使用批量插入而非单条插入(速度提升 10 倍+)。
    • 导出时启用临时文件缓存模式(.inMemory(false))。
  3. 异常处理

    • 在监听器中添加事务边界管理,避免长事务问题。
    • 导出文件名添加时间戳防重复:fileName + System.currentTimeMillis() + ".xlsx"

五、注意事项

  1. 大文件处理时建议增加进度提示(如每批次打印日志) 
  2. 导出时若需追加数据,应复用同一个 ExcelWriter 对象 
  3. 事务注解 @Transactional 建议加在 Service 方法而非监听器 

http://www.niftyadmin.cn/n/5865754.html

相关文章

Java Set实现类面试题

Java Set实现类面试题 HashSet Q1: HashSet的实现原理是什么&#xff1f; HashSet是基于HashMap实现的&#xff0c;使用HashMap的key来存储元素&#xff0c;value统一使用一个Object对象。 public class HashSetPrincipleExample {// 模拟HashSet的基本实现public class Si…

bitcoinjs学习笔记2-P2SH

BitcoinJS 学习笔记 2 - P2SH 1.概述 1.1 P2SH 是什么&#xff1f; 1.1.1 定义&#xff1a;Pay to Script Hash P2SH 允许你将比特币发送到一个“脚本的哈希值”上&#xff0c;而不是像P2PKH直接发送到一个“公钥的哈希值”上即P2SH的地址的脚本的哈希值&#xff0c;而P2PKH的…

服务器能否拒绝非浏览器发起的HTTP请求?

互联网各领域资料分享专区(不定期更新): Sheet 前言 服务器可以采取多种方法来拒绝非浏览器发起的HTTP请求,但需要明确的是:HTTP协议本身并不限制客户端类型,任何符合协议规范的请求都会被处理。因此,拒绝非浏览器请求需依赖额外策略。 正文 一、基于请求头过滤 1、Us…

【备赛】点亮LED

LED部分的原理图 led前面有锁存器&#xff0c;这是为了防止led会受到lcd的干扰&#xff08;lcd也需要用到这些引脚&#xff09;。 每次想要对led操作&#xff0c;就需要先打开锁存器&#xff0c;再执行操作&#xff0c;最后关闭锁存器。 这里需要注意的是&#xff0c;引脚配置…

mysql 学习17 SQL 锁

概述 全局锁 通过全局锁 进行数据备份 表级锁 表锁 元数据锁 意向锁 原先 A线程 开启了一个事务&#xff0c;udpate id 3的数据&#xff0c;就会有一个行级锁&#xff0c;锁定第三行 这时候如果B线程要 lock tables 这个表 read&#xff0c;那么理论上就要锁定这一行表。 那…

8.spring对logback的支持

文章目录 一、入口二、源码解析LoggingApplicationListener 三、其它支持四、总结 本节以logback为背景介绍的 一、入口 gav: org.springframework.boot:spring-boot:3.3.4 spring.factories文件中有如下两个配置 org.springframework.boot.logging.LoggingSystemFactory\ …

【Java 8】Lambda表达式介绍

目录 1、Lambda简介 2、语法介绍 3、Lambda表达式示例 3.1、无参数的 Lambda 表达式 3.2、单个参数的 Lambda 表达式 3.3、多个参数的 Lambda 表达式 3.4、带语句块的 Lambda 表达式 4、Lambda使用场景 4.1、替代匿名内部类 4.2、集合操作 4.3、排序 4.4、函数式接口…

Java使用EasyExcel实现异步导出

以下是使用 EasyExcel 工具类实现异步导出功能的 Demo&#xff0c;包括用户发起导出请求后&#xff0c;系统先返回响应&#xff0c;后台读取数据并上传至 COS&#xff0c;最后通知用户下载的完整流程。 实现步骤 用户发起导出请求 前端调用导出接口&#xff0c;后端立即返回响应…