Java使用Apache POI和EasyExcel读取Excel文件的实现方案

 更新时间:2025年12月26日 09:05:03   作者:hellotutu  
Java 读取 Excel 文件核心依赖 Apache POI(兼容 .xls(Excel 97-2003)和 .xlsx(Excel 2007+))或 EasyExcel(阿里开源,低内存、高性能),以下是两种主流方案的完整实现,需要的朋友可以参考下

Java 读取 Excel 文件核心依赖 Apache POI(兼容 .xls(Excel 97-2003)和 .xlsx(Excel 2007+))或 EasyExcel(阿里开源,低内存、高性能),以下是两种主流方案的完整实现,覆盖「读取简单单元格、读取指定sheet、读取表头+数据」等场景。

一、前置准备:引入依赖

方案 1:Apache POI(功能全,兼容所有Excel版本)

Maven 依赖(需同时引入 poipoi-ooxml,分别对应 .xls.xlsx):

<dependencies>
    <!-- 核心依赖:处理 .xls -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.5</version> <!-- 推荐最新稳定版 -->
    </dependency>
    <!-- 处理 .xlsx -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.5</version>
    </dependency>
    <!-- 可选:简化日期格式处理 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-schemas</artifactId>
        <version>4.1.2</version>
    </dependency>
</dependencies>

方案 2:EasyExcel(阿里开源,低内存,推荐大数据量)

Maven 依赖(仅需核心包,自动兼容 .xls/.xlsx):

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version> <!-- 最新版 -->
</dependency>
<!-- 可选:日志依赖(EasyExcel 依赖 slf4j) -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.9</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>2.0.9</version>
</dependency>

二、方案 1:Apache POI 读取 Excel(通用场景)

场景 1:读取所有sheet的所有单元格(基础版)

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import java.io.FileInputStream;
import java.io.IOException;

public class POIExcelReader {
    public static void main(String[] args) {
        String filePath = "D:/test.xlsx"; // 替换为你的Excel文件路径
        // 区分 .xls 和 .xlsx
        Workbook workbook = null;
        try (FileInputStream fis = new FileInputStream(filePath)) {
            if (filePath.endsWith(".xlsx")) {
                workbook = new XSSFWorkbook(fis); // .xlsx
            } else if (filePath.endsWith(".xls")) {
                workbook = new HSSFWorkbook(fis); // .xls
            } else {
                throw new IllegalArgumentException("不支持的Excel格式");
            }

            // 遍历所有sheet
            for (Sheet sheet : workbook) {
                System.out.println("===== Sheet名称:" + sheet.getSheetName() + " =====");
                // 遍历所有行(跳过表头:从第1行开始,rowNum=1)
                for (Row row : sheet) {
                    // 遍历该行所有单元格
                    for (Cell cell : row) {
                        // 获取单元格值(统一格式)
                        String cellValue = getCellValue(cell);
                        System.out.print(cellValue + "\t");
                    }
                    System.out.println(); // 换行
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (workbook != null) {
                try {
                    workbook.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 工具方法:统一处理不同类型的单元格值
    private static String getCellValue(Cell cell) {
        if (cell == null) {
            return "";
        }
        switch (cell.getCellType()) {
            case STRING: // 字符串
                return cell.getStringCellValue();
            case NUMERIC: // 数字/日期
                if (DateUtil.isCellDateFormatted(cell)) {
                    // 日期类型
                    return cell.getDateCellValue().toString();
                } else {
                    // 数字类型(避免科学计数法)
                    return String.valueOf(cell.getNumericCellValue());
                }
            case BOOLEAN: // 布尔值
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA: // 公式
                return cell.getCellFormula() + " = " + cell.getCachedFormulaResultType();
            case BLANK: // 空单元格
                return "";
            default:
                return "";
        }
    }
}

场景 2:读取指定sheet和指定行(精准读取)

// 读取指定sheet(索引从0开始,或按名称)
Sheet sheet = workbook.getSheetAt(0); // 第一个sheet
// 或 Sheet sheet = workbook.getSheet("用户数据"); // 按名称

// 读取指定行(如第2行,rowNum=1)
Row targetRow = sheet.getRow(1);
if (targetRow != null) {
    // 读取指定单元格(如第3列,cellNum=2)
    Cell targetCell = targetRow.getCell(2);
    String value = getCellValue(targetCell);
    System.out.println("指定单元格值:" + value);
}

// 遍历有效行(跳过空行)
int lastRowNum = sheet.getLastRowNum(); // 最后一行索引
for (int rowNum = 1; rowNum <= lastRowNum; rowNum++) {
    Row row = sheet.getRow(rowNum);
    if (row == null) {
        continue; // 跳过空行
    }
    // 读取该行单元格
    String name = getCellValue(row.getCell(0)); // 第1列:姓名
    String age = getCellValue(row.getCell(1));  // 第2列:年龄
    System.out.println("姓名:" + name + ",年龄:" + age);
}

三、方案 2:EasyExcel 读取 Excel(高性能,推荐大数据量)

EasyExcel 无需加载整个Excel到内存,适合读取十万级以上数据,核心是通过「监听器」逐行读取。

步骤 1:定义数据实体(与Excel表头映射)

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

// 对应Excel的表头:姓名、年龄、手机号
@Data // Lombok注解,自动生成get/set
public class UserExcelDTO {
    // value:Excel表头名称,index:列索引(可选)
    @ExcelProperty(value = "姓名", index = 0)
    private String name;

    @ExcelProperty(value = "年龄", index = 1)
    private Integer age;

    @ExcelProperty(value = "手机号", index = 2)
    private String phone;
}

步骤 2:自定义监听器(处理读取到的数据)

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;

// 自定义监听器,逐行读取数据并存储
public class UserExcelListener extends AnalysisEventListener<UserExcelDTO> {
    // 存储读取到的数据
    private List<UserExcelDTO> dataList = new ArrayList<>();

    // 每读取一行数据触发
    @Override
    public void invoke(UserExcelDTO user, AnalysisContext context) {
        dataList.add(user);
        System.out.println("读取到数据:" + user);
        // 可在此处批量处理(如每1000条插入数据库)
        if (dataList.size() >= 1000) {
            handleData(); // 处理数据
            dataList.clear(); // 清空
        }
    }

    // 所有数据读取完成后触发
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        handleData(); // 处理剩余数据
        System.out.println("Excel读取完成,总数据量:" + dataList.size());
    }

    // 数据处理逻辑(如插入数据库)
    private void handleData() {
        if (!dataList.isEmpty()) {
            // TODO: 批量插入数据库/业务处理
            System.out.println("批量处理" + dataList.size() + "条数据");
        }
    }

    // 获取读取到的所有数据
    public List<UserExcelDTO> getDataList() {
        return dataList;
    }
}

步骤 3:读取Excel文件(核心代码)

import com.alibaba.excel.EasyExcel;

import java.util.List;

public class EasyExcelReader {
    public static void main(String[] args) {
        String filePath = "D:/test.xlsx";
        // 初始化监听器
        UserExcelListener listener = new UserExcelListener();

        // 读取Excel(指定文件路径、实体类、监听器)
        EasyExcel.read(filePath, UserExcelDTO.class, listener)
                .sheet("用户数据") // 指定sheet名称(可选,默认第一个)
                .headRowNumber(1) // 表头行数(默认1行)
                .doRead(); // 执行读取

        // 获取所有数据
        List<UserExcelDTO> dataList = listener.getDataList();
        System.out.println("最终读取到的数据:" + dataList);
    }
}

四、关键注意事项

1. 文件路径与权限

  • 确保文件路径无中文/空格(避免 FileNotFoundException);
  • 若读取服务器文件,需保证Java进程有文件读取权限(如Linux下 chmod 755)。

2. 版本兼容

  • .xls(HSSFWorkbook)最大支持65536行,.xlsx(XSSFWorkbook)无行数限制;
  • EasyExcel 自动兼容两种格式,无需手动区分。

3. 性能优化

  • Apache POI 读取大数据量Excel易内存溢出,需用 SXSSFWorkbook(流式读取);
  • EasyExcel 天生适合大数据量,无需额外配置。

4. 日期/数字格式

  • Apache POI 需手动判断日期格式(DateUtil.isCellDateFormatted);
  • EasyExcel 可通过 @ExcelProperty(converter = DateConverter.class) 自定义格式转换。

五、两种方案对比

方案优点缺点适用场景
Apache POI功能全、兼容所有Excel特性大数据量易内存溢出、代码繁琐小数据量、需操作复杂Excel(公式/宏)
EasyExcel低内存、代码简洁、高性能不支持宏/复杂公式大数据量、普通数据读取(推荐)

六、常见问题解决

1.FileNotFoundException

  • 检查文件路径是否正确(绝对路径/相对路径);
  • 检查文件是否被占用(如Excel未关闭)。

2. 内存溢出(OOM)

  • Apache POI:改用 SXSSFWorkbook 流式读取;
  • 优先使用 EasyExcel。

3. 日期读取为数字

  • Apache POI:通过 DateUtil.isCellDateFormatted 判断并转换;
  • EasyExcel:配置日期转换器。

核心原则:小数据量/复杂Excel用 Apache POI,大数据量/普通读取用 EasyExcel;读取时务必处理空单元格和格式转换,避免空指针/格式错误。

以上就是Java使用Apache POI和EasyExcel读取Excel文件的实现方案的详细内容,更多关于Java Apache POI和EasyExcel读取Excel的资料请关注脚本之家其它相关文章!

相关文章

  • spring mvc路径匹配原则详解

    spring mvc路径匹配原则详解

    这篇文章主要介绍了spring mvc路径匹配原则详解,小编觉得还是挺不错的,这里分享给大家,需要的朋友可以参考下,下面就和小编一起来看看吧
    2018-02-02
  • SpringAop @Aspect织入不生效,不执行前置增强织入@Before方式

    SpringAop @Aspect织入不生效,不执行前置增强织入@Before方式

    这篇文章主要介绍了SpringAop @Aspect织入不生效,不执行前置增强织入@Before方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Servlet实现文件的上传与下载

    Servlet实现文件的上传与下载

    这篇文章主要为大家详细介绍了Servlet实现文件的上传与下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-03-03
  • SpringBoot2 整合MinIO中间件实现文件便捷管理功能

    SpringBoot2 整合MinIO中间件实现文件便捷管理功能

    这篇文章主要介绍了SpringBoot2 整合MinIO中间件,实现文件便捷管理,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • Java设计模式之访问者模式

    Java设计模式之访问者模式

    这篇文章介绍了Java设计模式之访问者模式,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-10-10
  • Java实现简易生产者消费者模型过程解析

    Java实现简易生产者消费者模型过程解析

    这篇文章主要介绍了Java实现简易生产者消费者模型过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • Java线程并发工具类CountDownLatch原理及用法

    Java线程并发工具类CountDownLatch原理及用法

    这篇文章主要介绍了Java线程并发工具类CountDownLatch原理及用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • Java数据结构及算法实例:插入排序 Insertion Sort

    Java数据结构及算法实例:插入排序 Insertion Sort

    这篇文章主要介绍了Java数据结构及算法实例:插入排序 Insertion Sort,本文直接给出实例代码,代码中包含详细注释,需要的朋友可以参考下
    2015-06-06
  • JPA @ManyToMany 报错StackOverflowError的解决

    JPA @ManyToMany 报错StackOverflowError的解决

    这篇文章主要介绍了JPA @ManyToMany 报错StackOverflowError的解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • JavaWeb pageContext对象原理解析

    JavaWeb pageContext对象原理解析

    这篇文章主要介绍了JavaWeb pageContext对象原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02

最新评论