浅谈 Apache POI:XSSFWorkbook 的原理与实践(Java 操作 Excel 实践指南)

在企业开发中,Excel 几乎无处不在

  • 数据导入(批量导入用户、工单、设备)
  • 报表导出(运营报表、财务报表)
  • 数据交换(系统之间通过 Excel 交互)

如果使用 Java 开发系统,最常见的解决方案就是:

Apache POI

而在 POI 中,最核心的类之一就是:

java 复制代码
org.apache.poi.xssf.usermodel.XSSFWorkbook

下面我们详细展开。


一、Apache POI 是什么

Apache POI 是 Apache 开源的一个 Java 操作 Office 文件的库

支持:

Office文件 POI组件
Excel 2003 .xls HSSF
Excel 2007+ .xlsx XSSF
Word .docx XWPF
PowerPoint XSLF

本文重点介绍:

java 复制代码
XSSFWorkbook

含义:

复制代码
XSSF = XML Spreadsheet Format

即:

用于操作 Excel .xlsx 文件的 Java API


二、Excel 的对象结构

Excel 文件其实是一个 层级结构

复制代码
Workbook(工作簿)
 ├── Sheet(工作表)
 │     ├── Row(行)
 │     │     ├── Cell(单元格)

Apache POI 的类结构:

Excel概念 POI类
工作簿 XSSFWorkbook
工作表 XSSFSheet
XSSFRow
单元格 XSSFCell

对象关系:

复制代码
XSSFWorkbook
    └── XSSFSheet
          └── XSSFRow
                └── XSSFCell

因此:

XSSFWorkbook 就是整个 Excel 文件的 Java 对象表示。


三、Excel 文件的真实结构(核心原理)

大家或许不知道:

.xlsx 本质上是一个 ZIP 文件。

我们可以直接解压:

复制代码
example.xlsx

得到:

复制代码
example.xlsx
 ├── [Content_Types].xml
 ├── _rels/
 ├── xl/
 │    ├── workbook.xml
 │    ├── worksheets/
 │    │      ├── sheet1.xml
 │    │      ├── sheet2.xml
 │    ├── styles.xml
 │    ├── sharedStrings.xml

核心结构:

文件 作用
workbook.xml 工作簿信息
sheet.xml 工作表
sharedStrings.xml 字符串池
styles.xml 样式

POI 的工作流程:

复制代码
Excel文件
    ↓
解压 ZIP
    ↓
解析 XML
    ↓
构建 Java 对象

最终形成:

复制代码
XSSFWorkbook
 └── XSSFSheet
      └── XSSFRow
           └── XSSFCell

四、引入 Apache POI 依赖

Maven 依赖:

xml 复制代码
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.5</version>
</dependency>

核心组件:

复制代码
poi
poi-ooxml
xmlbeans
commons-compress

五、读取 Excel(最常见场景)

典型场景:

  • 用户上传 Excel
  • 解析数据
  • 写入数据库

示例 Excel:

复制代码
users.xlsx

ID | Name | Age
1  | Tom  | 20
2  | Jack | 22

Java 读取代码:

java 复制代码
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;

public class ExcelReader {

    public static void main(String[] args) throws Exception {

        FileInputStream fis = new FileInputStream("users.xlsx");

        XSSFWorkbook workbook = new XSSFWorkbook(fis);

        Sheet sheet = workbook.getSheetAt(0);

        for (Row row : sheet) {

            for (Cell cell : row) {

                System.out.print(cell.toString() + "  ");
            }

            System.out.println();
        }

        workbook.close();
        fis.close();
    }
}

输出:

复制代码
ID Name Age
1 Tom 20
2 Jack 22

六、Excel 精确读取

实际开发中需要:

  • 跳过标题
  • 判断类型
  • 处理空值

示例:

java 复制代码
for (int i = 1; i <= sheet.getLastRowNum(); i++) {

    Row row = sheet.getRow(i);

    if (row == null) {
        continue;
    }

    Cell idCell = row.getCell(0);
    Cell nameCell = row.getCell(1);
    Cell ageCell = row.getCell(2);

    int id = (int) idCell.getNumericCellValue();
    String name = nameCell.getStringCellValue();
    int age = (int) ageCell.getNumericCellValue();

    System.out.println(id + " " + name + " " + age);
}

输出:

复制代码
1 Tom 20
2 Jack 22

七、创建 Excel 文件

除了读取,还可以 生成 Excel 报表

示例:

java 复制代码
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;

public class ExcelWriter {

    public static void main(String[] args) throws Exception {

        XSSFWorkbook workbook = new XSSFWorkbook();

        Sheet sheet = workbook.createSheet("Users");

        Row header = sheet.createRow(0);

        header.createCell(0).setCellValue("ID");
        header.createCell(1).setCellValue("Name");
        header.createCell(2).setCellValue("Age");

        Row row1 = sheet.createRow(1);
        row1.createCell(0).setCellValue(1);
        row1.createCell(1).setCellValue("Tom");
        row1.createCell(2).setCellValue(20);

        FileOutputStream fos = new FileOutputStream("users.xlsx");

        workbook.write(fos);

        fos.close();
        workbook.close();
    }
}

生成 Excel:

复制代码
users.xlsx

ID | Name | Age
1  | Tom  | 20

八、SpringBoot Excel 导出接口

真实系统中,通常通过 HTTP 下载 Excel

示例:

java 复制代码
@GetMapping("/export")
public void export(HttpServletResponse response) throws Exception {

    XSSFWorkbook workbook = new XSSFWorkbook();
    Sheet sheet = workbook.createSheet("Users");

    Row header = sheet.createRow(0);
    header.createCell(0).setCellValue("ID");
    header.createCell(1).setCellValue("Name");

    Row row = sheet.createRow(1);
    row.createCell(0).setCellValue(1);
    row.createCell(1).setCellValue("Tom");

    response.setContentType(
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

    response.setHeader(
        "Content-Disposition",
        "attachment;filename=users.xlsx");

    workbook.write(response.getOutputStream());

    workbook.close();
}

浏览器会下载:

复制代码
users.xlsx

九、大文件 Excel 的内存问题

如果 Excel 很大:

复制代码
100万行

使用XSSFWorkbook会全部加载到内存,可能导致:

复制代码
OutOfMemoryError

原因:

复制代码
POI 会把所有 Cell 对象加载到 JVM

十、大文件解决方案:SXSSFWorkbook

POI 提供了:

复制代码
SXSSFWorkbook

特点:

特点 说明
流式写入 不全部加载
低内存 适合百万级
滑动窗口 自动清理

示例:

java 复制代码
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

SXSSFWorkbook workbook = new SXSSFWorkbook(100);

含义:

复制代码
只保留 100 行在内存

生成百万数据:

java 复制代码
for (int i = 0; i < 1000000; i++) {

    Row row = sheet.createRow(i);

    row.createCell(0).setCellValue(i);
}

十一、Excel 导入最佳实践

生产环境建议:

1 跳过空行

java 复制代码
if (row == null) {
    continue;
}

2 判断单元格类型

java 复制代码
switch (cell.getCellType()) {

    case STRING:
        value = cell.getStringCellValue();
        break;

    case NUMERIC:
        value = cell.getNumericCellValue();
        break;
}

3 统一读取工具

建议封装:

java 复制代码
public static String getCellValue(Cell cell)

避免大量判断。


十二、XSSFWorkbook vs HSSFWorkbook

Excel版本 文件格式
HSSFWorkbook Excel 2003 .xls
XSSFWorkbook Excel 2007+ .xlsx

区别:

特点 HSSF XSSF
最大行 65536 1048576
最大列 256 16384
文件格式 二进制 XML

现代系统 基本全部使用 XSSFWorkbook


十三、总结

XSSFWorkbook 的本质:

Excel .xlsx 文件在 Java 中的对象表示。

核心结构:

复制代码
XSSFWorkbook
    ↓
XSSFSheet
    ↓
XSSFRow
    ↓
XSSFCell

常见用途:

场景 说明
Excel 导入 解析数据
Excel 导出 生成报表
数据交换 系统间导入导出

关键注意点:

  • 大文件使用 SXSSFWorkbook
  • 注意 Cell 类型判断
  • 建议封装 读取工具类
相关推荐
翊谦12 小时前
Java Agent开发 Milvus 向量数据库安装
java·数据库·milvus
晓晓hh12 小时前
JavaSE学习——迭代器
java·开发语言·学习
查古穆13 小时前
栈-有效的括号
java·数据结构·算法
Java面试题总结13 小时前
Spring - Bean 生命周期
java·spring·rpc
硅基诗人13 小时前
每日一道面试题 10:synchronized 与 ReentrantLock 的核心区别及生产环境如何选型?
java
014-code13 小时前
String.intern() 到底干了什么
java·开发语言·面试
codeJinger14 小时前
【Python】操作Excel文件
python·excel
摇滚侠14 小时前
JAVA 项目教程《苍穹外卖-12》,微信小程序项目,前后端分离,从开发到部署
java·开发语言·vue.js·node.js
楚国的小隐士14 小时前
为什么说Rust是对自闭症谱系人士友好的编程语言?
java·rust·编程·对比·自闭症·自闭症谱系障碍·神经多样性
春花秋月夏海冬雪14 小时前
代码随想录刷题 - 贪心Part1
java·算法·贪心·代码随想录