杰瑞科技汇

Java POI如何高效操作Excel?

  1. 环境准备:如何添加 POI 依赖。
  2. 核心概念Workbook, Sheet, Row, Cell 的关系。
  3. 详细操作:从创建、读取、修改到写入 Excel 文件的完整流程。
  4. 不同版本的区别.xls (HSSF) 和 .xlsx (XSSF) 的处理方式。
  5. 实用技巧:样式、日期处理、大文件优化等。

环境准备 (Maven)

如果你使用 Maven,在 pom.xml 文件中添加 POI 的依赖,根据你处理的 Excel 版本选择不同的依赖。

处理 .xlsx (Office 2007+) 格式 (推荐)

这是目前最主流的格式,推荐使用。

<dependencies>
    <!-- POI 核心库 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.3</version> <!-- 请使用最新版本 -->
    </dependency>
    <!-- POI 对 OOXML (Office Open XML) 格式的支持 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.3</version>
    </dependency>
    <!-- 用于处理 XML 的依赖,通常包含在 poi-ooxml 中,但显式声明更清晰 -->
    <dependency>
        <groupId>org.apache.xmlbeans</groupId>
        <artifactId>xmlbeans</artifactId>
        <version>5.1.1</version>
    </dependency>
    <!-- 用于解压 OOXML 包中文件的依赖 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-lite</artifactId>
        <version>5.2.3</version>
    </dependency>
</dependencies>

处理 .xls (Office 97-2003) 格式 (旧版)

如果你的项目需要兼容旧版 Excel,需要额外添加 poi-scratchpad 依赖。

<!-- 如果需要处理 .xls 格式,添加此依赖 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-scratchpad</artifactId>
    <version>5.2.3</version>
</dependency>

核心概念

理解 POI 操作 Excel 的核心对象模型至关重要,它们之间是层层包含的关系:

  • Workbook (工作簿):代表一个 Excel 文件,它是所有表格的顶级容器。
    • 对于 .xlsx 文件,使用 XSSFWorkbook
    • 对于 .xls 文件,使用 HSSFWorkbook
  • Sheet (工作表):代表 Workbook 中的一个工作表,"Sheet1"。
  • Row (行):代表 Sheet 中的某一行,行号从 0 开始。
  • Cell (单元格):代表 Row 中的一个单元格,列号也从 0 开始。

关系图Workbook -> Sheet -> Row -> Cell


详细操作示例

下面我们通过代码示例来演示最常见的操作。

示例1:创建并写入一个 .xlsx 文件

这个例子将创建一个新的 Excel 文件,写入一些数据,并设置单元格样式。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class CreateExcelExample {
    public static void main(String[] args) {
        // 1. 创建一个 XSSFWorkbook 对象,代表一个 .xlsx 文件
        Workbook workbook = new XSSFWorkbook();
        // 2. 创建一个工作表,名称为 "员工信息"
        Sheet sheet = workbook.createSheet("员工信息");
        // 3. 创建样式 (可选)
        Font headerFont = workbook.createFont();
        headerFont.setBold(true);
        headerFont.setFontHeightInPoints((short) 14);
        headerFont.setColor(IndexedColors.RED.getIndex());
        CellStyle headerStyle = workbook.createCellStyle();
        headerStyle.setFont(headerFont);
        headerStyle.setAlignment(HorizontalAlignment.CENTER);
        // 4. 创建表头行 (第 0 行)
        Row headerRow = sheet.createRow(0);
        String[] headers = {"ID", "姓名", "年龄", "入职日期", "薪资"};
        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
            cell.setCellStyle(headerStyle);
        }
        // 5. 创建数据行
        // 第二行数据
        Row dataRow1 = sheet.createRow(1);
        dataRow1.createCell(0).setCellValue(1);
        dataRow1.createCell(1).setCellValue("张三");
        dataRow1.createCell(2).setCellValue(28);
        dataRow1.createCell(3).setCellValue(new java.util.Date()); // 日期类型
        dataRow1.createCell(4).setCellValue(8500.50);
        // 第三行数据
        Row dataRow2 = sheet.createRow(2);
        dataRow2.createCell(0).setCellValue(2);
        dataRow2.createCell(1).setCellValue("李四");
        dataRow2.createCell(2).setCellValue(32);
        dataRow2.createCell(3).setCellValue(new java.util.Date());
        dataRow2.createCell(4).setCellValue(12000.00);
        // 6. 自动调整列宽以适应内容
        for (int i = 0; i < headers.length; i++) {
            sheet.autoSizeColumn(i);
        }
        // 7. 将工作簿写入文件
        try (FileOutputStream fileOut = new FileOutputStream("D:/temp/员工信息.xlsx")) {
            workbook.write(fileOut);
            System.out.println("Excel 文件创建成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 8. 关闭工作簿,释放资源
            try {
                workbook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

示例2:读取一个 .xlsx 文件

这个例子演示如何读取已有的 Excel 文件内容。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class ReadExcelExample {
    public static void main(String[] args) {
        String filePath = "D:/temp/员工信息.xlsx";
        try (FileInputStream fileIn = new FileInputStream(filePath);
             Workbook workbook = new XSSFWorkbook(fileIn)) {
            // 1. 获取第一个工作表
            Sheet sheet = workbook.getSheetAt(0); // 或者 workbook.getSheet("员工信息");
            // 2. 遍历每一行 (从第一行开始,跳过表头)
            for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row row = sheet.getRow(rowNum);
                if (row == null) {
                    continue; // 跳过空行
                }
                // 3. 遍历每个单元格
                Cell idCell = row.getCell(0);
                Cell nameCell = row.getCell(1);
                Cell ageCell = row.getCell(2);
                Cell dateCell = row.getCell(3);
                Cell salaryCell = row.getCell(4);
                // 4. 获取单元格值,并处理不同类型
                int id = (int) idCell.getNumericCellValue();
                String name = nameCell.getStringCellValue();
                int age = (int) ageCell.getNumericCellValue();
                // 日期处理
                DataFormatter dataFormatter = new DataFormatter(); // 可以直接获取单元格显示的字符串值
                String dateStr = dataFormatter.formatCellValue(dateCell);
                java.util.Date date = dateCell.getDateCellValue();
                double salary = salaryCell.getNumericCellValue();
                System.out.printf("ID: %d, 姓名: %s, 年龄: %d, 入职日期: %s / %s, 薪资: %.2f%n",
                        id, name, age, dateStr, date, salary);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意:读取单元格时,最好先判断单元格类型 (cell.getCellType()),或者使用 DataFormatter 来安全地获取字符串形式的值,避免因类型不匹配而出错。

示例3:修改一个已有的 .xlsx 文件

POI 不能直接修改文件,它的工作流程是:读取 -> 修改 -> 写入新文件

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ModifyExcelExample {
    public static void main(String[] args) {
        String inputFilePath = "D:/temp/员工信息.xlsx";
        String outputFilePath = "D:/temp/员工信息_修改后.xlsx";
        try (FileInputStream fileIn = new FileInputStream(inputFilePath);
             Workbook workbook = new XSSFWorkbook(fileIn);
             FileOutputStream fileOut = new FileOutputStream(outputFilePath)) {
            // 1. 获取工作表
            Sheet sheet = workbook.getSheet("员工信息");
            // 2. 修改第二行(索引为1)的数据
            Row rowToModify = sheet.getRow(1);
            if (rowToModify != null) {
                // 修改姓名
                Cell nameCell = rowToModify.getCell(1);
                nameCell.setCellValue("张三(已更新)");
                // 修改薪资
                Cell salaryCell = rowToModify.getCell(4);
                salaryCell.setCellValue(9000.00);
            }
            // 3. 在文件末尾添加新一行
            int lastRowNum = sheet.getLastRowNum();
            Row newRow = sheet.createRow(lastRowNum + 1);
            newRow.createCell(0).setCellValue(3);
            newRow.createCell(1).setCellValue("王五");
            newRow.createCell(2).setCellValue(25);
            newRow.createCell(3).setCellValue(new java.util.Date());
            newRow.createCell(4).setCellValue(7500.0);
            // 4. 将修改后的内容写入新文件
            workbook.write(fileOut);
            System.out.println("Excel 文件修改成功,已保存为: " + outputFilePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

.xls vs .xlsx 的区别

特性 .xls (HSSF) .xlsx (XSSF)
文件格式 二进制格式,基于 OLE 复合文档。 Office Open XML (OOXML) 格式,本质上是 ZIP 压缩包。
最大行数 65,536 行 (2^16) 1,048,576 行 (2^20)
最大列数 256 列 (2^8) 16,384 列 (2^14)
内存占用 相对较低,因为是纯内存操作。 相对较高,特别是处理大文件时。
处理方式 HSSFWorkbook XSSFWorkbook
大文件支持 不支持,内存易溢出。 支持,有 SXSSF (Streaming Usermodel) 模式。

实用技巧

1 处理大文件 (SXSSF)

当处理超过 10,000 行数据时,使用 XSSFWorkbook 会导致内存占用过高,甚至 OutOfMemoryError,此时应使用 SXSSFWorkbook (Streaming Usermodel)。

特点

  • 它会将不活跃的行(超出滑动窗口大小的行)写入临时文件,从而保持内存占用在一个较低的水平。
  • 它是 XSSFWorkbook 的一个子集,不支持所有功能(如单元格样式、公式计算等)。

示例

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import java.io.FileOutputStream;
import java.io.IOException;
public class LargeExcelExample {
    public static void main(String[] args) throws IOException {
        // 1. 创建 SXSSFWorkbook,参数是滑动窗口大小 (100)
        Workbook workbook = new SXSSFWorkbook(100);
        Sheet sheet = workbook.createSheet("大数据");
        // 2. 写入 100,000 行数据
        for (int i = 0; i < 100000; i++) {
            Row row = sheet.createRow(i);
            row.createCell(0).setCellValue("ID " + i);
            row.createCell(1).setCellValue("Name " + i);
        }
        // 3. 写入文件
        try (FileOutputStream out = new FileOutputStream("D:/temp/大数据.xlsx")) {
            workbook.write(out);
        } finally {
            // 4. 清理临时文件 (非常重要!)
            ((SXSSFWorkbook) workbook).dispose();
            workbook.close();
        }
        System.out.println("大文件生成完成!");
    }
}

2 日期处理

Excel 有自己的日期系统,直接用 java.util.Date 写入,Excel 可能会将其识别为数字,需要设置单元格类型为 CellType.NUMERIC 并应用日期格式。

CellStyle dateStyle = workbook.createCellStyle();
CreationHelper createHelper = workbook.getCreationHelper();
dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-MM-dd"));
Cell dateCell = row.createCell(3);
dateCell.setCellValue(new java.util.Date()); // 写入日期
dateCell.setCellStyle(dateStyle); // 应用日期格式

3 单元格类型判断

在读取时,务必判断单元格类型,否则容易出错。

Cell cell = row.getCell(0);
switch (cell.getCellType()) {
    case STRING:
        System.out.println(cell.getStringCellValue());
        break;
    case NUMERIC:
        if (DateUtil.isCellDateFormatted(cell)) {
            System.out.println(cell.getDateCellValue());
        } else {
            System.out.println(cell.getNumericCellValue());
        }
        break;
    case BOOLEAN:
        System.out.println(cell.getBooleanCellValue());
        break;
    case FORMULA:
        System.out.println(cell.getCellFormula());
        break;
    default:
        System.out.println("未知类型");
}

POI 是一个非常强大且灵活的 Java Excel 操作库,掌握其核心的 Workbook -> Sheet -> Row -> Cell 模型,并根据 .xls.xlsx 的区别选择合适的实现类,你就可以应对绝大多数 Excel 文件的读写和修改任务,对于大文件场景,请务必使用 SXSSFWorkbook 来优化内存。

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