如何针对大Excel做文件读取?

针对大Excel文件(如超过百万行)的读取,传统的一次性加载到内存的方式会导致 内存溢出(OOM) ,需采用 流式读取(Streaming)分块读取(Chunk) 的策略。以下是具体方案及优化建议:


一、核心解决方案

1. 使用 Apache POI 的 SAX 模式(事件驱动)
  • 适用场景 :处理 .xlsx 文件(不支持 .xls),逐行解析避免内存溢出。

  • 代码示例(Java)

    java 复制代码
    import org.apache.poi.openxml4j.opc.OPCPackage;
    import org.apache.poi.xssf.eventusermodel.XSSFReader;
    import org.apache.poi.xssf.usermodel.XSSFSheetXMLHandler;
    import org.xml.sax.InputSource;
    import org.xml.sax.XMLReader;
    import org.xml.sax.helpers.XMLReaderFactory;
    
    public class BigExcelReader {
        public void read(String filePath) throws Exception {
            try (OPCPackage pkg = OPCPackage.open(filePath)) {
                XSSFReader reader = new XSSFReader(pkg);
                XMLReader parser = XMLReaderFactory.createXMLReader();
                // 自定义 Sheet 处理器(需实现 XSSFSheetXMLHandler.SheetContentsHandler)
                SheetHandler handler = new SheetHandler();
                parser.setContentHandler(new XSSFSheetXMLHandler(reader.getStylesTable(), null, handler, false));
                InputSource sheetSource = new InputSource(reader.getSheetsData().next());
                parser.parse(sheetSource);
            }
        }
    
        // 自定义处理器(处理每一行数据)
        private static class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
            @Override
            public void row(int rowNum, List<Object> rowData) {
                // 处理行数据(rowData 是单元格值的列表)
                System.out.println("Row " + rowNum + ": " + rowData);
            }
            // 其他方法(如 startRow、endRow)可空实现
        }
    }
  • 优点:内存占用极低(约几十MB),适合处理超大文件。

  • 缺点:需要手动处理复杂的单元格格式和公式。


2. 使用 EasyExcel(阿里开源的流式读取库)
  • 适用场景 :简化流式读取,支持 .xlsx.xls

  • 步骤

    1. 添加依赖 (Maven):

      xml 复制代码
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>easyexcel</artifactId>
          <version>3.3.2</version>
      </dependency>
    2. 定义数据模型

      java 复制代码
      @Data // Lombok 注解,生成 getter/setter
      public class UserData {
          @ExcelProperty("姓名") // 对应表头
          private String name;
          @ExcelProperty("年龄")
          private Integer age;
      }
    3. 流式读取

      java 复制代码
      public class ReadBigExcel {
          public static void main(String[] args) {
              String fileName = "large_file.xlsx";
              // 逐行读取(每读一行触发一次监听器)
              EasyExcel.read(fileName, UserData.class, new AnalysisEventListener<UserData>() {
                  @Override
                  public void invoke(UserData data, AnalysisContext context) {
                      // 处理单行数据(如存入数据库)
                      System.out.println(data);
                  }
                  @Override
                  public void doAfterAllAnalysed(AnalysisContext context) {
                      System.out.println("读取完成");
                  }
              }).sheet().doRead();
          }
      }
  • 优点:API 简单,内存可控(默认每批次读取1000行),支持数据模型映射。

  • 缺点:无法直接操作单元格样式。


3. 分页读取(结合 POI 的 XSSF 和 SXSSF)
  • 适用场景:需要分批次处理数据(如导出到数据库)。

  • 示例代码

    java 复制代码
    Workbook workbook = WorkbookFactory.create(new File("large_file.xlsx"));
    Sheet sheet = workbook.getSheetAt(0);
    int pageSize = 1000; // 每页读取1000行
    int totalRows = sheet.getPhysicalNumberOfRows();
    
    for (int page = 0; page < (totalRows + pageSize - 1) / pageSize; page++) {
        int startRow = page * pageSize;
        int endRow = Math.min(startRow + pageSize, totalRows);
        for (int i = startRow; i < endRow; i++) {
            Row row = sheet.getRow(i);
            // 处理行数据
        }
        // 手动触发垃圾回收(可选)
        System.gc();
    }
  • 优点:代码简单直观。

  • 缺点:不适合超大文件(如百万行以上),内存仍可能溢出。


4. 转换为 CSV 处理
  • 适用场景:不需要保留 Excel 格式(如公式、样式)。
  • 步骤
    1. 使用工具(如 Apache POI)将 Excel 转换为 CSV。
    2. 用 OpenCSV 或 Commons CSV 流式读取 CSV。
  • 优点:CSV 处理速度快,内存占用低。
  • 缺点:丢失 Excel 特有功能(如多Sheet、公式)。

二、优化技巧

1. 内存优化
  • 禁用公式计算

    java 复制代码
    Workbook workbook = WorkbookFactory.create(file, null, true); // 避免缓存公式结果
  • 关闭缓存

    java 复制代码
    FileInputStream fis = new FileInputStream(file);
    StreamingReader reader = StreamingReader.builder()
        .rowCacheSize(100)    // 缓存行数
        .bufferSize(4096)     // 缓冲区大小
        .open(fis);
2. 性能优化
  • 跳过空行和隐藏行:在处理器中判断行数据是否为空。
  • 多线程处理:将读取的数据分块后提交到线程池处理(注意线程安全)。
3. 异常处理
  • 捕获处理异常 :避免因某一行数据错误导致整个任务中断。

    java 复制代码
    try {
        // 处理行数据
    } catch (Exception e) {
        e.printStackTrace(); // 记录错误日志
    }

三、工具推荐

工具/库 适用场景 特点
Apache POI 需要精细控制Excel格式 功能全,但内存消耗高
EasyExcel 快速处理数据(无需复杂格式) 内存低,API简单
OpenCSV 处理纯数据(非Excel特有功能) 速度极快
Pandas Python环境下的数据分析 适合中小型文件(需优化)

四、实战建议

  1. 预处理文件:拆分大文件为多个小文件(如按Sheet或行数拆分)。
  2. 监控内存:使用 JVisualVM 或 Arthas 监控内存使用情况。
  3. 日志记录:记录处理进度和错误行号,便于排查问题。
  4. 测试极限:在预发布环境测试文件的最大承载能力。

通过以上方法,可有效处理GB级Excel文件,避免内存溢出问题。

相关推荐
hPw0eKIqD16 小时前
使用二次封装的Excel COM 组件操作Excel\WPS ET中的区域、行和列
excel·wps
冰心孤城1 天前
Excel: xls与xlsx格式转换排坑指南
java·前端·excel
金豆呀1 天前
WPS批量提取Word文档内容生成固定格式Excel表格
word·excel·wps
Access开发易登软件1 天前
Access 怎么做前后端分离?用 Web API 读写 SQL Server
前端·数据库·人工智能·microsoft·excel·access
红莲凪是2 天前
使用二次封装的Excel COM 组件操作Excel\WPS ET IExcelRange 高级应用
python·excel·wps
用户298698530142 天前
如何使用免费工具在线将 Excel 转换为 XML 格式
人工智能·后端·excel
开开心心就好2 天前
内存清理工具定时自动清理开机自启动
java·开发语言·elasticsearch·ocr·excel·音视频·big data
开开心心就好2 天前
电脑内存优化工具一键释放内存超简单
java·开发语言·macos·arcgis·ocr·excel·音视频
随手工具-Excel, pdf, SQL2 天前
没有 SSMS 导入向导?Excel/CSV 一键生成 SQL Server INSERT 脚本
excel·sql server·在线工具·insert·测试数据·ssms·t-sql
SunnyDays10113 天前
Python 将 Excel(XLS/XLSX)转换为 JSON:导出工作簿、工作表、单元格区域与自定义 JSON 结构
python·json·excel·excel 转 json·导出 excel 到 json·xlsx 转 json·xls 转 json