如何针对大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文件,避免内存溢出问题。

相关推荐
杜子腾dd6 小时前
17.使用读写包操作Excel文件:pyxlsb 包
python·数据挖掘·excel·numpy·pandas
YUELEI1188 小时前
使用htool工具导出和导入Excel表
excel
storyfull8 小时前
Excel(函数篇):IF函数、FREQUNCY函数、截取函数、文本处理函数、日期函数、常用函数详解
职场和发展·excel·学习方法
石榴花上8 小时前
mysql数据库中多张表导出成excel方式
数据库·mysql·excel
storyfull8 小时前
Excel(函数篇):Vlookup函数 详细用法
excel·创业创新·学习方法
初级代码游戏12 小时前
VSTO(C#)Excel开发 系列目录 含源码发布
c#·excel·源码·vba·vsto
初级代码游戏12 小时前
VSTO(C#)Excel开发13:实现定时器
c#·excel·vba·定时器·vsto
bu_shuo21 小时前
Excel单元格中插入自定义超链接
excel
meisongqing21 小时前
DeepSeek + Excel:数据处理专家 具体步骤
excel·deepseek