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

相关推荐
帮帮志3 小时前
Python-pandas-操作Excel文件(读取数据/写入数据)及Excel表格列名操作详细分享
python·excel·pandas
liupenglove3 小时前
一个读写excel的简单程序(golang)
数据仓库·后端·golang·excel
xll_0074 小时前
使用Python在excel里创建柱状图
python·excel
課代表13 小时前
Excel 数组功能及应用示例
excel
龙虎榜小红牛系统13 小时前
pandas读取Excel数据(.xlsx和.xls)到treeview
excel·pandas
学海泛舟翁15 小时前
EXCEL中跨行匹配两组数据
excel
专注于大数据技术栈19 小时前
pyspark将hive数据写入Excel文件中
hive·hadoop·python·excel
PowerBI学谦1 天前
Copilot for Excel 一键词云分析与情绪分析
excel·copilot
.卡1 天前
(007)Excel 公式的使用
excel
xun-ming1 天前
Java导出带图片的Excel
java·excel·easypoi·导出·excel带图片