目录
- 准备工作:引入 POI 依赖
- 基础入门:导出一个简单的
.xls文件 (HSSFWorkbook) - 进阶:导出
.xlsx文件 (XSSFWorkbook) - 常用功能详解
- 创建复杂表头(合并单元格)
- 设置单元格样式(字体、颜色、边框、对齐方式)
- 设置列宽和行高
- 写入日期和数字格式
- 数据量大时的优化:SXSSFWorkbook (流式 API)
- 完整示例:带样式的复杂 Excel 导出
- 总结与注意事项
准备工作:引入 POI 依赖
你需要在你的项目中添加 POI 的依赖,如果你使用 Maven,在 pom.xml 文件中添加以下依赖。

重要提示: POI 的不同模块需要分开引入。
<dependencies>
<!-- 核心 POI 库,用于处理 .xls 和 .xlsx -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.4</version>
</dependency>
<!-- 用于处理 .xlsx 格式的 OOXML 架构 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-lite</artifactId>
<version>5.2.4</version>
</dependency>
</dependencies>
依赖版本说明:
poi: 处理旧版 Excel 格式(.xls)。poi-ooxml: 处理新版 Office Open XML 格式(.xlsx),它依赖于poi。poi-ooxml-lite: 一个轻量级的poi-ooxml,如果你不需要 XSLF(PowerPoint)等模块,可以使用它来减少最终 jar 包的大小。
基础入门:导出一个简单的 .xls 文件 (HSSFWorkbook)
.xls 是 Excel 97-2003 的格式,使用 HSSFWorkbook 类,由于行数和列数限制(最多 65536 行,256 列),现在已不推荐用于生产环境,但它是理解 POI 工作原理的好起点。
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class SimpleXlsExport {
public static void main(String[] args) {
// 1. 创建一个 HSSFWorkbook 对象,代表一个 Excel 文件
Workbook workbook = new HSSFWorkbook();
// 2. 创建一个 Sheet,名为 "Sheet1"
Sheet sheet = workbook.createSheet("用户数据");
// 3. 创建表头行(第 0 行)
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("ID");
headerRow.createCell(1).setCellValue("姓名");
headerRow.createCell(2).setCellValue("年龄");
// 4. 创建数据行
Row dataRow1 = sheet.createRow(1);
dataRow1.createCell(0).setCellValue(1);
dataRow1.createCell(1).setCellValue("张三");
dataRow1.createCell(2).setCellValue(25);
Row dataRow2 = sheet.createRow(2);
dataRow2.createCell(0).setCellValue(2);
dataRow2.createCell(1).setCellValue("李四");
dataRow2.createCell(2).setCellValue(30);
// 5. 写入文件
try (FileOutputStream fileOut = new FileOutputStream("D:/temp/users.xls")) {
workbook.write(fileOut);
System.out.println("users.xls 文件生成成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 6. 关闭 workbook 对象,释放资源
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
进阶:导出 .xlsx 文件 (XSSFWorkbook)
.xlsx 是目前主流的 Excel 格式,它基于 XML,没有行数和列数的严格限制(受限于内存),我们使用 XSSFWorkbook 类。

代码与 .xls 版本几乎完全相同,只需替换 HSSFWorkbook 为 XSSFWorkbook,并修改输出文件名。
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class SimpleXlsxExport {
public static void main(String[] args) {
// 1. 创建一个 XSSFWorkbook 对象
Workbook workbook = new XSSFWorkbook();
// 2. 创建 Sheet
Sheet sheet = workbook.createSheet("产品列表");
// 3. 创建表头
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("产品ID");
headerRow.createCell(1).setCellValue("产品名称");
headerRow.createCell(2).setCellValue("价格");
// 4. 创建数据
Row dataRow1 = sheet.createRow(1);
dataRow1.createCell(0).setCellValue("P001");
dataRow1.createCell(1).setCellValue("笔记本电脑");
dataRow1.createCell(2).setCellValue(5999.99);
// 5. 写入文件
try (FileOutputStream fileOut = new FileOutputStream("D:/temp/products.xlsx")) {
workbook.write(fileOut);
System.out.println("products.xlsx 文件生成成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 6. 关闭 workbook
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
常用功能详解
1 创建复杂表头(合并单元格)
使用 sheet.addMergedRegion() 方法来合并单元格。
// 假设在 sheet 对象上操作
// 合并第一行的第0列到第2列,创建一个大的表头
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 2));
Row bigHeaderRow = sheet.createRow(0);
bigHeaderRow.createCell(0).setCellValue("用户信息总表");
// 合并第二行的第0列和第1列,创建二级表头
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 1));
Row subHeaderRow = sheet.createRow(1);
subHeaderRow.createCell(0).setCellValue("基本信息");
subHeaderRow.createCell(2).setCellValue("年龄");
2 设置单元格样式
样式是 POI 中比较重要的部分,通过 CellStyle 类来设置。
import org.apache.poi.ss.usermodel.*;
// 1. 创建一个样式对象
CellStyle style = workbook.createCellStyle();
// 2. 设置字体
Font font = workbook.createFont();
font.setFontName("Arial");
font.setFontHeightInPoints((short) 12); // 字号
font.setBold(true); // 加粗
style.setFont(font);
// 3. 设置对齐方式
style.setAlignment(HorizontalAlignment.CENTER); // 水平居中
style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
// 4. 设置边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
// 5. 应用样式到单元格
Cell cell = headerRow.createCell(0);
cell.setCellValue("ID");
cell.setCellStyle(style);
3 设置列宽和行高
// 设置第一列的宽度,宽度是以字符个数为单位的 sheet.setColumnWidth(0, 15 * 256); // 15个字符宽 // 设置第二列的宽度为自适应内容 sheet.autoSizeColumn(1); // 设置第三行的高度,高度是以 1/20 个点为单位的 sheet.getRow(2).setHeight((short) (30 * 20));
4 写入日期和数字格式
使用 DataFormat 来设置单元格的数据格式。

import java.util.Date;
// 创建一个日期格式
CellStyle dateStyle = workbook.createCellStyle();
CreationHelper createHelper = workbook.getCreationHelper();
dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-MM-dd"));
// 创建一个数字格式(保留两位小数)
CellStyle numberStyle = workbook.createCellStyle();
numberStyle.setDataFormat(createHelper.createDataFormat().getFormat("#.00"));
// 应用样式
Row row = sheet.createRow(3);
Cell dateCell = row.createCell(0);
dateCell.setCellValue(new Date());
dateCell.setCellStyle(dateStyle);
Cell numberCell = row.createCell(1);
numberCell.setCellValue(123.456);
numberCell.setCellStyle(numberStyle);
数据量大时的优化:SXSSFWorkbook
当导出数据量非常大(例如几十万行)时,使用 XSSFWorkbook 会导致内存溢出,因为 XSSFWorkbook 会将所有数据都加载到内存中。
SXSSFWorkbook (Streaming Usermodel API) 是 POI 提供的解决方案,它采用“滑动窗口”的机制,只保留一部分数据在内存中,其余数据临时写入磁盘,从而大大减少内存消耗。
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class LargeDataExport {
public static void main(String[] args) {
// 1. 创建 SXSSFWorkbook,参数 100 表示内存中保留的行数
// 当超过这个数量时,早先的数据会被写入临时文件
Workbook workbook = new SXSSFWorkbook(100);
// 2. 创建 Sheet
Sheet sheet = workbook.createSheet("大数据报表");
// 3. 模拟写入 10 万行数据
for (int i = 0; i < 100000; i++) {
Row row = sheet.createRow(i);
row.createCell(0).setCellValue("ID_" + i);
row.createCell(1).setCellValue("用户名称_" + i);
// 在控制台打印进度
if (i % 10000 == 0) {
System.out.println("已写入 " + i + " 行数据...");
}
}
// 4. 写入文件
try (FileOutputStream fileOut = new FileOutputStream("D:/temp/large_data.xlsx")) {
workbook.write(fileOut);
System.out.println("large_data.xlsx 文件生成成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5. 清理临时文件(非常重要!)
((SXSSFWorkbook) workbook).dispose();
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意:
SXSSFWorkbook只支持.xlsx格式。- 必须调用
dispose()方法来删除在磁盘上生成的临时文件。 SXSSFWorkbook的一些功能(如sheet.setColumnWidth)在数据量极大时可能会有性能问题,或者需要提前设置。
完整示例:带样式的复杂 Excel 导出
下面是一个综合了前面所有知识点的完整示例,导出一个包含合并单元格、多种样式、日期和数字格式的复杂 Excel 文件。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
public class ComplexExcelExport {
public static void main(String[] args) {
// 1. 创建 Workbook
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("销售报表");
// 2. 创建样式
// 标题样式
CellStyle titleStyle = createTitleStyle(workbook);
// 表头样式
CellStyle headerStyle = createHeaderStyle(workbook);
// 日期样式
CellStyle dateStyle = createDateStyle(workbook);
// 金额样式
CellStyle currencyStyle = createCurrencyStyle(workbook);
// 3. 写入数据
writeData(sheet, titleStyle, headerStyle, dateStyle, currencyStyle);
// 4. 调整列宽
sheet.autoSizeColumn(0);
sheet.autoSizeColumn(1);
sheet.setColumnWidth(2, 20 * 256); // 固定日期列宽
sheet.autoSizeColumn(3);
// 5. 写入文件
try (FileOutputStream fileOut = new FileOutputStream("D:/temp/sales_report.xlsx")) {
workbook.write(fileOut);
System.out.println("sales_report.xlsx 文件生成成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void writeData(Sheet sheet, CellStyle titleStyle, CellStyle headerStyle, CellStyle dateStyle, CellStyle currencyStyle) {
// 合并单元格创建大标题
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 3));
Row titleRow = sheet.createRow(0);
titleRow.createCell(0).setCellValue("2025年第四季度销售报告");
titleRow.getCell(0).setCellStyle(titleStyle);
// 创建二级表头
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 1));
Row subHeaderRow = sheet.createRow(1);
subHeaderRow.createCell(0).setCellValue("订单信息");
subHeaderRow.getCell(0).setCellStyle(headerStyle);
subHeaderRow.createCell(2).setCellValue("日期");
subHeaderRow.getCell(2).setCellStyle(headerStyle);
subHeaderRow.createCell(3).setCellValue("金额");
subHeaderRow.getCell(3).setCellStyle(headerStyle);
// 写入数据行
for (int i = 2; i < 12; i++) {
Row dataRow = sheet.createRow(i);
dataRow.createCell(0).setCellValue("ORD-" + (1000 + i));
dataRow.createCell(1).setCellValue("商品名称 " + i);
dataRow.createCell(2).setCellValue(new Date());
dataRow.getCell(2).setCellStyle(dateStyle);
dataRow.createCell(3).setCellValue(1000.00 * i);
dataRow.getCell(3).setCellStyle(currencyStyle);
}
}
private static CellStyle createTitleStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 16);
font.setBold(true);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
private static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
private static CellStyle createDateStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setDataFormat(workbook.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));
return style;
}
private static CellStyle createCurrencyStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setDataFormat(workbook.createDataFormat().getFormat("#,##0.00"));
style.setAlignment(HorizontalAlignment.RIGHT);
return style;
}
}
总结与注意事项
- 格式选择:优先使用
XSSFWorkbook(.xlsx) 格式,因为它更现代、功能更强、没有行数限制,仅在需要兼容旧版 Excel (.xls) 时使用HSSFWorkbook。 - 内存管理:对于大数据量导出,必须使用
SXSSFWorkbook,并记得在最后调用dispose()方法清理临时文件。 - 样式复用:创建样式对象(
CellStyle,Font)时,尽量在循环外创建,然后在循环中重复使用,而不是为每个单元格都创建一个新样式,这样可以显著提高性能并减少内存占用。 - 关闭资源:使用
try-with-resources语句(如try (FileOutputStream...))来确保文件流被正确关闭,或者在finally块中手动关闭,防止资源泄漏。 - 中文乱码问题:如果导出的 Excel 文件用 Excel 打开时出现乱码,通常是因为代码文件的编码或 POI 内部处理问题,确保你的 Java 源文件是
UTF-8编码,POI 版本较新(5.x 版本对 Unicode 支持很好),在 Web 应用中,设置正确的Content-Type和Content-Disposition响应头也很重要。
