当前位置: 首页 > news >正文

EasyExcel 流式处理中实现末尾行过滤的技术方案

背景

在处理大规模 Excel 数据导入时,常遇到文件末尾包含汇总行的情况。这类汇总数据不应被导入业务表,但在流式处理模式下,传统的"读取总行数再减N"的方式无法应用。本文介绍一种基于延迟缓冲区的解决方案。


问题分析

传统方案的局限性

方案一:全量加载后过滤

List<Row> allRows = readAllRows(file);
for (int i = 0; i < allRows.size() - skipEndRows; i++) {process(allRows.get(i));
}

缺陷:违背流式处理的设计初衷,大文件场景下易导致 OOM。


方案二:直接流式处理

void invoke(RowData row) {// 边读边处理,但无法判断当前行是否为末尾行
}

缺陷:在逐行回调模式下,当前行的位置信息不足以判断其是否属于需要跳过的末尾区间。


技术方案

核心思想

采用固定窗口延迟缓冲机制:维护一个大小为 skipEndRows 的滑动窗口,仅处理窗口溢出的数据,确保末尾 N 行始终保留在缓冲区内,直到文件读取完毕后被自动丢弃。

实现原理

  1. 使用 LinkedList 作为延迟缓冲区
  2. 每读取一行数据,追加至缓冲区尾部
  3. 当缓冲区大小超过 skipEndRows 时,从队列头部移除一行并提交处理
  4. 文件读取完成后,缓冲区内剩余的 skipEndRows 行数据自动随对象销毁而丢弃

流程示意图

skipEndRows = 1 为例,处理包含 5 行数据 + 1 行汇总的文件:

阶段触发事件delayBuffer 状态batchRowData操作说明
1invoke(row1)[row1][]size=1 ≤ skipEndRows,暂不处理
2invoke(row2)[row2][row1]size=2 > 1,移除 row1 加入批次
3invoke(row3)[row3][row1, row2]移除 row2
4invoke(row4)[row4][row1, row2, row3]移除 row3
5invoke(row5)[row5][row1, ..., row4]移除 row4
6invoke(汇总行)[汇总][..., row5]移除 row5,汇总行滞留
7doAfterAllAnalysed()[汇总][]处理剩余批次,不处理缓冲区
8GC 回收null-缓冲区对象销毁,汇总行丢弃

代码实现

抽象基类核心逻辑

protected void processExcelFileStreaming(Path filePath, int batchSize, BiConsumer<Map<Integer, String>, List<Map<Integer, String>>> batchProcessor) throws Exception {final Map<Integer, String> headerRowMap = new HashMap<>();final List<Map<Integer, String>> batchRowData = new ArrayList<>(batchSize);final int skipEndRows = skipEndRowCount();final LinkedList<Map<Integer, String>> delayBuffer = new LinkedList<>();EasyExcel.read(filePath.toFile(), new AnalysisEventListener<Map<Integer, Object>>() {@Overridepublic void invoke(Map<Integer, Object> data, AnalysisContext context) {// 省略:表头解析、空行过滤等预处理逻辑Map<Integer, String> rowData = convertToStringMap(data);// 延迟缓冲机制:入队delayBuffer.add(rowData);// 当缓冲区溢出时,出队并提交处理if (delayBuffer.size() > skipEndRows) {Map<Integer, String> eligibleRow = delayBuffer.removeFirst();batchRowData.add(eligibleRow);// 批次满时触发回调if (batchRowData.size() >= batchSize) {batchProcessor.accept(headerRowMap, new ArrayList<>(batchRowData));batchRowData.clear();}}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 处理批次尾部数据if (!batchRowData.isEmpty()) {batchProcessor.accept(headerRowMap, new ArrayList<>(batchRowData));batchRowData.clear();}// 关键:delayBuffer 内的 skipEndRows 行数据在此被隐式丢弃}}).sheet(getTargetSheetIndex()).headRowNumber(0).doRead();
}

模板方法设计

public abstract class AbstractBaseExcelParser {/*** 钩子方法:返回需跳过的末尾行数* 子类可覆写以适配不同业务场景*/protected int skipEndRowCount() {return 0;  // 默认不跳过}
}@Component
public class FinancialStatementParser extends AbstractBaseExcelParser {@Overrideprotected int skipEndRowCount() {return 1;  // 跳过汇总行}
}

技术细节

EasyExcel 回调时机分析

doAfterAllAnalysed() 的触发条件

EasyExcel 内部实现伪代码:

public void doRead() {int totalRows = sheet.getLastRowNum();  // Excel 内部元数据记录总行数for (int i = 0; i <= totalRows; i++) {Row row = sheet.getRow(i);listener.invoke(parseRow(row), context);}// 循环结束后触发listener.doAfterAllAnalysed(context);
}

Excel 文件结构说明

.xlsx 文件本质是 ZIP 压缩包,其中 xl/worksheets/sheet1.xml 包含元数据:

<worksheet><dimension ref="A1:C1001"/>  <!-- 数据范围元信息 --><sheetData><row r="1">...</row><!-- ... --><row r="1001">...</row></sheetData>
</worksheet>

EasyExcel 解析 <dimension> 标签即可获知总行数,从而判断读取结束时机。


方案总结

优势

  1. 内存可控:占用量仅为 O(batchSize + skipEndRows),与文件大小无关
  2. 逻辑清晰:核心实现仅 3 行代码,可读性强
  3. 扩展性好:通过模板方法模式支持子类灵活配置跳过行数

适用场景

  • 大规模 Excel 数据导入
  • 文件末尾包含汇总/统计行
  • 内存受限环境
  • 需要边读边处理的流式场景

参考资料

  • EasyExcel 官方文档
http://www.dtcms.com/a/554290.html

相关文章:

  • 免费网址导航网站建设编程培训班学费是多少
  • 内推网站企业网站后台管理软件
  • 广州公司网站电脑全自动挂机赚钱
  • Redis 分布式锁如何保证同一时间只有一个客户端持有锁
  • 做网站编辑工作好不好如何设计好的网页
  • U-Net笔记
  • 海力士DDR差异性对比--H9HCNNNCPMMLXR-NEE H9HCNNNCPMMLXR-NEI
  • bfs|红黑树multiset
  • 伊利集团的网站建设水平评价成都做网站做的好的公司
  • 论文阅读:arxiv 2025 Safety in Large Reasoning Models: A Survey
  • 选择手机网站建设医疗网站织梦
  • 蓝牙体重秤方案:硬件设计需要注意什么
  • 张家港建网站的公司住房和城乡建设部网站
  • 【AIGC】HPS v2:评估人类对文本到图像合成偏好的可靠基准
  • Download from your IP address is not allowed(qt下载教程)
  • 出海东南亚无忧:腾讯云如何凭借本地合作与全球节点,保障游戏和电商业务合规流畅?
  • Jmeter的自动化测试实施方案详解
  • 共享自行车与电动共享自行车使用中建成环境影响的对比研究:基于合肥数据的时空机器学习分析
  • 如何使用Jmeter做接口测试?
  • 网站用哪个软件做企业官网建设费用
  • 重庆网站设计找重庆最佳科技蛋糕网站源码
  • 东莞建设网官方网站小程序怎么赚钱的
  • 石家庄网站开发公司电话佛山新网站建设渠道
  • Golang多goroutine求解1000万和1亿以内的素数
  • 【开题答辩过程】以《基于协同过滤算法的彩妆商城系统的设计与实现》为例,不会开题答辩的可以进来看看
  • 一级a做爰片i免费网站横沥镇仿做网站
  • 怎么做能上谷歌网站优化营商环境心得体会
  • 学习C#调用OpenXml操作word文档的基本用法(4:Style类分析-2)
  • 河北建设厅注册中心网站首页网站头页
  • Vue3教程简介