EasyExcel的简单读取操作

EasyExcel的简单读取操作

  • Java领域解析、生成Excel比较有名的框架有Apache poi、 jxl等。但他们都存在一个严重的问题就是
    非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或
    者JVM频繁的full gc。
  • EasyExcel是阿里巴巴开源的一个excel处理框架, 以使用简单、节省内存著称。 EasyExcel能大大减
    少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一
    行行读取数据,逐个解析。
  • EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理
    (AnalysisEventListener)。

摘自官网

导入依赖

这个版本之间有个对应关系,不然会报错。

                    <!--xls-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>3.17</version>
            </dependency>
            <!--xlsx-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>3.17
<think>我们正在讨论EasyExcel的ReadListener如何读取第一行数据。根据引用,尤其是引用[1]和引用[3],我们知道ReadListener可以在读取每行数据时进行自定义操作。通常,我们使用`PageReadListener`来实现分页读取,但这里我们关注的是读取第一行。 关键点: 1. EasyExcel读取流程是通过监听器(ReadListener)来逐行处理数据的。 2. 我们可以在监听器的`invoke`方法中获取每一行的数据。 3. 如果我们只想读取第一行,那么可以在读取第一行数据后做一些操作,比如保存下来,然后跳过后续行(但注意,EasyExcel本身没有提供直接停止读取的方法,但我们可以通过抛出异常来终止,不过这样不优雅,而且会抛出异常,所以不推荐)。 另一种思路:我们可以在监听器中设置一个标志,当读取第一行后,我们记录下数据,然后后续的行我们可以选择不处理(但读取过程仍然会继续,直到文件结束)。如果我们希望只读取第一行就停止,那么我们可以考虑在读取第一行后抛出异常来终止读取(虽然这样会抛出异常,但我们可以捕获这个异常,从而实现只读取第一行)。 然而,根据EasyExcel的设计,我们也可以使用`headRowNumber`来指定表头行数(比如1行),然后我们读取的第一行数据实际上是第二行(即数据的第一行)。但这里用户的需求是读取第一行(可能是表头或者数据的第一行)。 具体步骤: 假设我们要读取的是Excel文件的第一行(无论是表头还是数据): 1. 创建一个ReadListener,在它的`invoke`方法中,第一次被调用时就是第一行数据(注意:如果设置了表头行数,那么表头行不会进入监听器的`invoke`方法)。 2. 如果我们想读取的是Excel文件的第一行数据(包括表头),那么我们需要设置`headRowNumber(0)`,这样第一行就会作为数据行被读取。 因此,我们需要: - 使用`EasyExcel.read()`方法,并指定监听器。 - 在监听器中,我们只关心第一行数据,然后可以通过抛出异常来终止读取(在catch中忽略异常)或者记录下第一行后忽略后续行。 示例代码: 方案一:读取第一行(数据行,跳过表头)后终止读取(通过异常) 注意:这种方式会抛出异常,但我们可以捕获它,从而实现只读取第一行。 方案二:如果我们不需要终止,只需要第一行数据,那么我们可以在监听器中记录第一行,然后后续行忽略(但文件还是会全部读完,效率较低)。 根据引用[3],我们看到`PageReadListener`是分页处理的,但如果我们只需要第一行,也可以自己实现一个监听器。 下面我们采用自定义监听器,并在读取第一行后保存,然后立即抛出一个自定义异常(用于终止读取),同时捕获这个异常并不做处理(视为正常结束)。 步骤: 1. 自定义一个ReadListener,实现`ReadListener`接口(或者继承`AnalysisEventListener`)。 2. 在`invoke`方法中,如果是第一次调用(即第一行),则记录数据,然后抛出一个特定的异常(如`FirstRowReadException`)。 3. 在调用EasyExcel的read方法时,捕获这个异常,然后正常处理。 但是,EasyExcel的监听器中的异常会被捕获并包装成`ExcelAnalysisException`,所以我们需要在自定义异常中做标记。 然而,根据引用[4]中提到的问题,监听器没有反应,所以我们要确保代码正确。 另一种更简单的方法:我们可以在`invoke`方法中处理第一行数据,然后使用一个标志位,后续行不再处理(但读取仍然会继续)。这样不会中断读取,但可以避免处理后续行。对于小文件来说,可以接受;对于大文件,我们可能希望中断读取以节省时间。 考虑到用户可能希望中断读取,我们采用抛出异常的方式(虽然会抛出异常,但我们可以捕获并忽略,只要达到目的即可)。 示例代码(中断方式): ```java import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.exception.ExcelAnalysisStopException; public class FirstRowDataListener<T> extends AnalysisEventListener<T> { private T firstRowData; @Override public void invoke(T data, AnalysisContext context) { // 获取第一行数据 firstRowData = data; // 读取到第一行后,立即抛出停止读取的异常 throw new ExcelAnalysisStopException("Stop after reading the first row"); } @Override public void doAfterAllAnalysed(AnalysisContext context) { // 不需要做任何事 } public T getFirstRowData() { return firstRowData; } } ``` 然后,使用这个监听器: ```java public static void main(String[] args) { String fileName = "example.xlsx"; FirstRowDataListener<YourDataModel> listener = new FirstRowDataListener<>(); try { EasyExcel.read(fileName, YourDataModel.class, listener).sheet().doRead(); } catch (ExcelAnalysisStopException e) { // 捕获到我们抛出的停止异常,表示已经读取到第一行,可以忽略 System.out.println("已经读取到第一行数据,停止读取"); } YourDataModel firstRowData = listener.getFirstRowData(); // 处理第一行数据... } ``` 注意: 1. `YourDataModel`是你定义的对应Excel一行的数据模型(使用注解映射)。 2. 这里我们利用了EasyExcel已有的异常类`ExcelAnalysisStopException`(这个异常是EasyExcel提供的,用于停止读取,不会打印堆栈跟踪,因为它是预期内的)。 但是,根据EasyExcel的文档,抛出`ExcelAnalysisStopException`是允许的,并且会被捕获,从而停止读取。这样我们就实现了只读取第一行。 如果我们想要读取的是Excel的第一行(通常是表头),那么我们需要读取表头行。在EasyExcel中,表头行不会进入`invoke`方法,而是会进入`invokeHead`方法。所以,如果我们想读取表头,需要重写`invokeHead`方法。 但是,用户的问题没有明确是第一行数据还是表头。假设用户想要的是数据的第一行(即表头下面的第一行),那么上述方法可行。如果用户想要的是表头(第一行),那么我们需要: ```java public class FirstHeadRowListener extends AnalysisEventListener<Object> { private Map<Integer, String> headMap; @Override public void invokeHead(Map<Integer, String> headMap, AnalysisContext context) { this.headMap = headMap; // 同样,我们可以在这里抛出异常来停止 throw new ExcelAnalysisStopException("Stop after reading the head row"); } @Override public void invoke(Object data, AnalysisContext context) { // 不会执行 } @Override public void doAfterAllAnalysed(AnalysisContext context) { } public Map<Integer, String> getHeadMap() { return headMap; } } ``` 使用方式类似。 因此,用户需要明确是要读取表头(第一行)还是数据的第一行(第二行)。根据问题描述,用户说“读取第一行数据”,可能是指数据的第一行(跳过表头),也可能是表头行。这里我们按照数据的第一行来处理(即跳过表头),因为通常表头是用于映射的,而数据行是我们需要的内容。 但是,如果用户想要读取的第一行是Excel文件的第一行(无论该行是表头还是数据),那么我们需要设置`headRowNumber`为0,这样第一行就会被当作数据行读取,然后我们用第一种监听器(处理数据行的)来读取第一行数据。 示例: ```java EasyExcel.read(fileName, YourDataModel.class, listener) // 设置headRowNumber为0,表示没有表头,第一行就是数据 .headRowNumber(0) .sheet() .doRead(); ``` 这样,Excel的第一行就会作为数据被读取到监听器的`invoke`方法中。 总结: 1. 如果用户想要读取的是Excel的第一行(作为数据行),设置headRowNumber(0),然后使用第一种监听器(读取第一行数据后停止)。 2. 如果用户想要读取的是表头行(第一行),则使用第二种监听器(重写invokeHead方法)并停止。 根据用户问题:“读取第一行数据”,我倾向于认为是数据的第一行(即表头之后的第一行),所以采用第一种方式,但不设置headRowNumber(0)(因为默认headRowNumber为1,即第一行是表头,第二行开始是数据)。那么,我们读取的第一行数据就是第二行(数据的第一行)。因此,我们不需要设置headRowNumber,只需要用第一种监听器,然后读取监听器中的第一行数据(即第二行)即可。 但是,如果用户想要的文件的第一行(也就是Excel的第一行)作为数据读取,那么就需要设置headRowNumber(0)。 因此,我们需要明确用户的需求: - 如果用户需求是读取Excel的第一行(无论表头还是数据),则设置headRowNumber(0),然后读取第一行数据。 - 如果用户需求是读取数据的第一行(跳过表头),那么默认headRowNumber(1),然后读取监听器中第一次调用的数据(即第二行)。 由于问题中说“第一行数据”,我理解为数据的第一行(跳过表头),所以采用默认设置(headRowNumber=1),然后读取监听器中的第一行数据(即第二行)。代码如下: ```java FirstRowDataListener<YourDataModel> listener = new FirstRowDataListener<>(); try { EasyExcel.read(fileName, YourDataModel.class, listener).sheet().doRead(); } catch (ExcelAnalysisStopException e) { // 忽略 } YourDataModel firstDataRow = listener.getFirstRowData(); ``` 注意:如果Excel只有一行(表头行),那么监听器不会被调用(因为没有数据行),所以`firstDataRow`为null。 因此,我们需要检查是否为null。 如果用户希望读取表头行,则使用第二种监听器。 由于问题没有明确,我们可以提供两种方案。但根据问题字面意思“读取第一行数据”,我理解为数据的第一行(跳过表头)。 下面,我们给出读取数据第一行(跳过表头)的完整示例代码。 步骤: 1. 定义数据模型(YourDataModel),使用EasyExcel的注解。 2. 定义监听器(如上第一种监听器,读取第一行数据后停止)。 3. 读取文件。 示例代码: 数据模型(假设有三列:姓名,年龄,邮箱): ```java @Data // 使用lombok public class YourDataModel { @ExcelProperty("姓名") private String name; @ExcelProperty("年龄") private Integer age; @ExcelProperty("邮箱") private String email; } ``` 监听器(同上,第一种): ```java public class FirstRowDataListener<T> extends AnalysisEventListener<T> { private T firstRowData; @Override public void invoke(T data, AnalysisContext context) { firstRowData = data; // 停止读取 throw new ExcelAnalysisStopException(); } @Override public void doAfterAllAnalysed(AnalysisContext context) { // 不需要 } public T getFirstRowData() { return firstRowData; } } ``` 读取: ```java public class ReadFirstRowExample { public static void main(String[] args) { String fileName = "example.xlsx"; FirstRowDataListener<YourDataModel> listener = new FirstRowDataListener<>(); try { EasyExcel.read(fileName, YourDataModel.class, listener).sheet().doRead(); } catch (ExcelAnalysisStopException e) { // 正常停止 } YourDataModel firstDataRow = listener.getFirstRowData(); if (firstDataRow != null) { System.out.println("第一行数据: " + firstDataRow); } else { System.out.println("文件没有数据行"); } } } ``` 这样,我们就读取了数据的第一行(即Excel的第二行)。 如果用户希望读取的是Excel的第一行(也就是表头行),那么我们可以修改为第二种监听器,并且不使用数据模型(因为表头是Map<Integer, String>): ```java public class FirstHeadRowListener extends AnalysisEventListener<Object> { private Map<Integer, String> headMap; @Override public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { this.headMap = headMap; throw new ExcelAnalysisStopException(); } @Override public void invoke(Object data, AnalysisContext context) { // 不会执行 } @Override public void doAfterAllAnalysed(AnalysisContext context) { } public Map<Integer, String> getHeadMap() { return headMap; } } ``` 然后读取: ```java FirstHeadRowListener listener = new FirstHeadRowListener(); try { // 这里不需要数据模型,因为我们要读的是表头,所以用Object.class EasyExcel.read(fileName, Object.class, listener).sheet().doRead(); } catch (ExcelAnalysisStopException e) { // 忽略 } Map<Integer, String> headMap = listener.getHeadMap(); System.out.println("表头: " + headMap); ``` 综上所述,根据用户的需求,我们可以提供两种方案。但用户问题中明确是“第一行数据”,所以优先提供第一种方案(读取数据的第一行)。 因此,回答如下: </think>要使用 EasyExcel 的 `ReadListener` 读取 Excel 的第一行数据,需结合表头设置和监听器逻辑。以下是具体实现方法: ### 📌 方法步骤 1. **设置表头行数** 通过 `headRowNumber(0)` 将第一行识别为**数据行**而非表头: ```java EasyExcel.read(filePath, YourDataModel.class, listener) .headRowNumber(0) // 关键:将第一行视为数据 .sheet() .doRead(); ``` 2. **监听器捕获第一行** 自定义监听器在 `invoke()` 方法中处理第一行数据后立即终止读取: ```java public class FirstRowListener extends AnalysisEventListener<YourDataModel> { private YourDataModel firstRowData; @Override public void invoke(YourDataModel data, AnalysisContext context) { // 捕获第一行数据 firstRowData = data; // 立即终止读取 throw new ExcelAnalysisStopException("已读取第一行"); } @Override public void doAfterAllAnalysed(AnalysisContext context) {} public YourDataModel getFirstRowData() { return firstRowData; } } ``` 3. **捕获终止异常** 调用时处理 `ExcelAnalysisStopException` 异常获取数据: ```java FirstRowListener listener = new FirstRowListener(); try { EasyExcel.read("data.xlsx", YourDataModel.class, listener) .headRowNumber(0) .sheet() .doRead(); } catch (ExcelAnalysisStopException e) { // 忽略终止异常 } YourDataModel firstRow = listener.getFirstRowData(); System.out.println("第一行数据: " + firstRow); ``` ### 🧩 关键说明 - **`headRowNumber(0)`**:强制将 Excel 第一行作为数据行解析(默认值为 1,首行会被识别为表头)[^1]。 - **抛出 `ExcelAnalysisStopException`**:这是 EasyExcel 内置异常,用于安全终止读取流程[^3]。 - **数据模型 `YourDataModel`**:需使用 `@ExcelProperty` 注解定义字段与列的映射关系。 ### ⚠️ 常见问题 若监听器无响应(如引用[4]所述): 1. 检查文件路径是否正确 2. 确认数据模型字段与 Excel 列对齐 3. 验证 `@ExcelProperty` 注解配置 4. 确保使用 `.headRowNumber(0)` 覆盖默认表头设置 > 示例完整代码可参考 [EasyExcel 官方文档](https://www.yuque.com/easyexcel/doc/read)。 --- ### 🔍 相关问题 1. **如何读取 Excel 前 N 行数据?** 修改监听器逻辑,在 `invoke()` 中计数达到 N 行后终止。 2. **表头和数据行分开处理的最佳实践是什么?** 使用默认 `headRowNumber(1)`,在 `invokeHeadMap()` 中处理表头,`invoke()` 中处理数据行[^1][^3]。 3. **大文件读取时如何避免内存溢出?** 采用 `PageReadListener` 分页读取(如引用[1]所述),并设置 JVM 调优参数 `-Xmx`。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值