杰瑞科技汇

Java解析Excel用POI怎么操作?

目录

  1. Apache POI 简介
  2. 环境准备
  3. 核心概念:XSSFWorkbook vs HSSFWorkbook
  4. 完整代码示例:读取 .xlsx (Excel 2007+) 文件
  5. 处理不同数据类型
  6. 读取 .xls (Excel 97-2003) 文件
  7. 处理大文件(SXSSFWorkbook
  8. 最佳实践与常见问题

Apache POI 简介

Apache POI (Poor Obfuscation Implementation) 是一个开源的 Java 库,由 Apache 软件基金会维护,它提供了 API,允许 Java 程序读取和写入 Microsoft Office 格式的文件,包括 Excel、Word、PowerPoint 等。

Java解析Excel用POI怎么操作?-图1
(图片来源网络,侵删)

对于 Excel,POI 主要支持两种格式:

  • .xls: Excel 97-2003 格式,使用 HSSFWorkbook 类。
  • .xlsx: Excel 2007 及以上版本格式,使用 XSSFWorkbook 类。

环境准备

你需要在你的项目中添加 POI 的依赖,如果你使用 Maven,请在 pom.xml 文件中添加以下依赖:

<dependencies>
    <!-- 用于处理 .xlsx (Excel 2007+) 格式 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.5</version> <!-- 建议使用最新稳定版 -->
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.5</version> <!-- 处理 OOXML 格式 (.xlsx, .docx) 必需 -->
    </dependency>
    <!-- 用于处理 .xls (Excel 97-2003) 格式 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-scratchpad</artifactId>
        <version>5.2.5</version>
    </dependency>
    <!-- 如果需要处理公式,可以添加这个 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-lite</artifactId>
        <version>5.2.5</version>
    </dependency>
</dependencies>

注意poi-ooxml 依赖内部会包含 poipoi-ooxml-lite,所以通常只需要 poipoi-ooxml 就能覆盖大部分场景。

核心概念:XSSFWorkbook vs HSSFWorkbook

  • XSSFWorkbook: 用于操作 .xlsx 格式的 Excel 文件,这是基于 OOXML (Office Open XML) 标准的,本质上是一个 ZIP 压缩包,里面包含 XML 文件,POI 解压后读取 XML 来获取数据。
  • HSSFWorkbook: 用于操作 .xls 格式的 Excel 文件,这是旧的二进制格式。

选择哪个?

Java解析Excel用POI怎么操作?-图2
(图片来源网络,侵删)
  • 优先使用 XSSFWorkbook,因为 .xlsx 是当前主流格式,且支持更大的行数和列数。
  • 如果你的应用需要兼容非常旧的 Excel 版本(2003年以前),才需要使用 HSSFWorkbook

完整代码示例:读取 .xlsx (Excel 2007+) 文件

假设我们有一个名为 student_info.xlsx 的文件,内容如下:

姓名 年龄 班级 入学日期
张三 20 一班 2025-09-01
李四 21 二班 2025-09-01
王五 22 一班 2025-09-01

下面是读取这个文件的完整 Java 代码:

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ExcelReader {
    public static void main(String[] args) {
        // 1. 定义 Excel 文件路径
        String excelFilePath = "path/to/your/student_info.xlsx"; // 请替换为你的文件路径
        // 使用 try-with-resources 语句自动关闭资源
        try (FileInputStream fis = new FileInputStream(excelFilePath);
             Workbook workbook = new XSSFWorkbook(fis)) {
            // 2. 获取第一个工作表
            Sheet sheet = workbook.getSheetAt(0); // 索引从0开始
            // 3. 获取标题行 (第一行)
            Row headerRow = sheet.getRow(0);
            if (headerRow == null) {
                System.out.println("Excel 文件为空或没有标题行。");
                return;
            }
            // 4. 遍历数据行 (从第二行开始)
            for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
                Row dataRow = sheet.getRow(rowIndex);
                if (dataRow == null) {
                    continue; // 跳过空行
                }
                // 5. 获取单元格数据并处理
                String name = getCellValueAsString(dataRow.getCell(0));
                int age = (int) dataRow.getCell(1).getNumericCellValue();
                String className = getCellValueAsString(dataRow.getCell(2));
                Date admissionDate = dataRow.getCell(3).getDateCellValue();
                // 格式化日期
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                String formattedDate = sdf.format(admissionDate);
                // 打印数据
                System.out.printf("姓名: %s, 年龄: %d, 班级: %s, 入学日期: %s%n",
                        name, age, className, formattedDate);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 将单元格内容安全地转换为字符串
     * @param cell 单元格对象
     * @return 单元格内容的字符串表示
     */
    private static String getCellValueAsString(Cell cell) {
        if (cell == null) {
            return "";
        }
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue().trim();
            case NUMERIC:
                // 如果是日期格式的数字,需要特殊处理
                if (DateUtil.isCellDateFormatted(cell)) {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                    return sdf.format(cell.getDateCellValue());
                } else {
                    // 处理普通数字,避免出现科学计数法
                    return String.valueOf((long) cell.getNumericCellValue());
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                // 如果是公式,可以获取公式的计算结果
                return cell.getCellFormula();
            case BLANK:
                return "";
            default:
                return "";
        }
    }
}

代码解析:

  1. try-with-resources: FileInputStreamWorkbook 都实现了 AutoCloseable 接口,使用 try-with-resources 可以确保它们在使用后被自动关闭,避免资源泄漏。
  2. Workbook workbook = new XSSFWorkbook(fis): 创建一个 XSSFWorkbook 对象,传入 FileInputStream 来加载 Excel 文件。
  3. Sheet sheet = workbook.getSheetAt(0): 获取文件中的第一个工作表(Sheet)。getSheet("Sheet1") 也可以通过名称获取。
  4. sheet.getLastRowNum(): 获取最后一行的索引(从0开始)。
  5. Row row = sheet.getRow(i): 获取指定行的 Row 对象。
  6. Cell cell = row.getCell(j): 获取指定列的 Cell 对象。
  7. cell.getCellType(): 判断单元格的数据类型,非常重要!因为 Excel 中一个单元格可能包含字符串、数字、日期、布尔值等。
  8. getCellValueAsString 方法: 这是一个辅助方法,用于将不同类型的单元格值统一转换为字符串,处理了 null、数字、日期等情况,使代码更健壮。

处理不同数据类型

正如代码中所示,处理单元格时必须考虑其类型。Cell 类提供了 getCellType() 方法,返回 CellType 枚举值:

  • STRING: 文本
  • NUMERIC: 数字或日期
  • BOOLEAN: 布尔值
  • FORMULA: 公式
  • BLANK: 空单元格
  • ERROR: 错误

特别注意数字和日期: NUMERIC 类型的单元格可能代表一个普通数字(如年龄 20),也可能代表一个日期(如入学日期),POI 提供了 DateUtil.isCellDateFormatted(cell) 来判断一个数字单元格是否被格式化为日期。

读取 .xls (Excel 97-2003) 文件

读取 .xls 文件的代码与 .xlsx 几乎完全相同,只需要将 XSSFWorkbook 替换为 HSSFWorkbook 即可。

// ...
try (FileInputStream fis = new FileInputStream("path/to/your/file.xls");
     Workbook workbook = new HSSFWorkbook(fis)) { // 注意这里使用 HSSFWorkbook
    // 后续代码与 .xlsx 完全相同
    Sheet sheet = workbook.getSheetAt(0);
    // ...
}
// ...

处理大文件(SXSSFWorkbook

当处理非常大的 Excel 文件(包含数十万行数据)时,XSSFWorkbook 会将整个文件加载到内存中,这很容易导致 OutOfMemoryError(内存溢出)。

为了解决这个问题,POI 提供了 SXSSFWorkbook(Streaming Usermodel API),它的工作原理是:

  1. 在内存中只保留一定数量的行(例如100行)。
  2. 当行数超过这个限制时,将较早的行临时写入到磁盘上的临时文件中。
  3. 这样,内存占用始终保持在一个较低的水平。

示例代码:

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class LargeExcelReaderExample {
    public static void main(String[] args) {
        // 模拟一个很大的数据源
        int rowCount = 100000; // 假设有10万行数据
        // 1. 创建 SXSSFWorkbook,参数指定内存中保留的行数
        // -1 表示使用默认值(通常是100)
        Workbook workbook = new SXSSFWorkbook(-1);
        // 2. 创建工作表
        Sheet sheet = workbook.createSheet("Large Data");
        // 3. 写入数据(这里只是演示如何写入,读取同理,但通常大文件是生成而不是读取)
        for (int i = 0; i < rowCount; i++) {
            Row row = sheet.createRow(i);
            for (int j = 0; j < 10; j++) {
                Cell cell = row.createCell(j);
                cell.setCellValue("Row " + i + ", Col " + j);
            }
        }
        // 4. 将数据写入到文件
        try (FileOutputStream out = new FileOutputStream("large_file.xlsx")) {
            workbook.write(out);
            System.out.println("大文件生成成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5. 重要!必须清理临时文件
            ((SXSSFWorkbook) workbook).dispose();
        }
    }
}

注意SXSSFWorkbook 主要用于生成大文件,对于读取大文件,POI 本身没有提供流式的读取 API,如果遇到读取超大文件且内存不足的问题,可能需要考虑:

  • 将文件分割成多个小文件。
  • 使用更底层的 API(如 Event APISAX API),但这会复杂很多。

最佳实践与常见问题

  • 始终使用 try-with-resources: 确保文件流和 Workbook 对象被正确关闭。
  • 检查 null: 在获取 RowCell 之前,先检查它们是否为 null,避免 NullPointerException
  • 处理日期类型: 使用 DateUtil.isCellDateFormatted() 来区分数字和日期。
  • 性能考虑: 对于读取操作,XSSFWorkbookHSSFWorkbook 的性能差异不大,对于写入操作,SXSSFWorkbook 是处理大文件的不二之选。
  • 文件路径: 使用绝对路径或确保文件相对于程序运行路径是正确的。
  • 文件损坏: Excel 文件格式不正确或已损坏,POI 可能会抛出异常,需要做好异常处理。
任务 适用场景
读取/写入 .xlsx XSSFWorkbook 现代 Excel 文件(2007+),主流选择。
读取/写入 .xls HSSFWorkbook 旧版 Excel 文件(97-2003)。
生成超大 .xlsx SXSSFWorkbook 生成包含海量数据的 Excel 文件,防止内存溢出。

通过以上教程,你应该已经掌握了使用 Apache POI 解析 Excel 文件的基本方法,从简单的读取到处理不同数据类型,再到应对大文件的挑战,这些知识足以应对大多数 Java 操作 Excel 的场景。

分享:
扫描分享到社交APP
上一篇
下一篇