Apache POI 本身并不直接支持 .docx (Office Open XML) 格式的复杂文档操作。

POI 的核心是处理 .xls (Excel) 和 .doc (Word 97-2003 格式) 的 HWPF 组件,对于现代的 .docx 格式,POI 提供了一个名为 XWPF 的子项目,它专门用于处理 Office Open XML 格式,包括 Word、Excel 和 PowerPoint。
当人们说“用 POI 导出 Word”时,通常指的就是使用 POI-XWPF 库。
下面我将为你提供一个详细的指南,从基础环境搭建到各种复杂场景的代码示例。
第一步:添加 Maven 依赖
在你的 pom.xml 文件中,添加 POI 的核心依赖和 XWPF 的依赖,请务必使用较新的稳定版本,因为老版本可能存在 Bug。

<dependencies>
<!-- POI 核心依赖 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.4</version>
</dependency>
<!-- 用于处理 .docx 格式的 XWPF 依赖 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.4</version>
</dependency>
<!-- 为了更好的处理 XML,可能需要这个依赖 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-lite</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
第二步:基础 Word 导出(创建、写入、保存)
这是一个最简单的例子,演示如何创建一个新的 Word 文档,写入一些文本,并保存到本地。
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import java.io.FileOutputStream;
import java.io.IOException;
public class SimpleWordExport {
public static void main(String[] args) {
// 1. 创建一个 XWPFDocument 对象,代表一个 Word 文档
XWPFDocument document = new XWPFDocument();
try (FileOutputStream out = new FileOutputStream("D:/simple_export.docx")) {
// 2. 创建一个段落
XWPFParagraph paragraph = document.createParagraph();
// 3. 在段落中创建一个文本运行
XWPFRun run = paragraph.createRun();
// 4. 设置文本内容
run.setText("你好,这是使用 Apache POI 导出的第一个 Word 文档!");
// 5. 可以设置字体样式
run.setBold(true);
run.setFontSize(20);
run.setColor("FF0000"); // 红色
// 6. 创建第二个段落
XWPFParagraph paragraph2 = document.createParagraph();
XWPFRun run2 = paragraph2.createRun();
run2.setText("这是第二段文本。");
run2.setItalic(true);
// 7. 将文档写入输出流
document.write(out);
System.out.println("Word 文档生成成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 8. 关闭文档
try {
if (document != null) {
document.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
第三步:高级功能示例
实际应用中,导出的 Word 往往包含表格、列表、图片、页眉页脚等,下面我们逐一介绍。
插入表格
// ... 在创建文档之后 ...
// 创建一个 3行3列 的表格
XWPFTable table = document.createTable(3, 3);
// 获取第一行
XWPFTableRow row1 = table.getRow(0);
// 获取该行的第一个单元格
XWPFTableCell cell1 = row1.getCell(0);
// 在单元格中创建一个文本运行并设置内容
cell1.addParagraph().createRun().setText("姓名");
XWPFTableCell cell2 = row1.getCell(1);
cell2.addParagraph().createRun().setText("年龄");
XWPFTableCell cell3 = row1.getCell(2);
cell3.addParagraph().createRun().setText("职业");
// 填充第二行数据
XWPFTableRow row2 = table.getRow(1);
row2.getCell(0).setText("张三");
row2.getCell(1).setText("30");
row2.getCell(2).setText("软件工程师");
// 填充第三行数据
XWPFTableRow row3 = table.getRow(2);
row3.getCell(0).setText("李四");
row3.getCell(1).setText("28");
row3.getCell(2).setText("产品经理");
插入列表
// ... 创建段落之后 ...
XWPFParagraph listParagraph = document.createParagraph();
// 设置为项目列表
listParagraph.setNumID(1); // 注意:这个 numID 需要预先定义,或者使用 CTNumPr
// 更简单的方式是手动添加符号
XWPFRun listRun = listParagraph.createRun();
listRun.setText("• 第一项列表内容");
listRun.addCarriageReturn(); // 换行
XWPFRun listRun2 = listParagraph.createRun();
listRun2.setText("• 第二项列表内容");
注意:创建复杂的自动编号或项目符号列表比较麻烦,通常需要操作底层的 CTNumPr 对象,对于简单需求,手动添加 或 是最快的方法。
插入图片
// ... 创建段落之后 ...
XWPFParagraph imageParagraph = document.createParagraph();
XWPFRun imageRun = imageParagraph.createRun();
// 图片路径
String imagePath = "D:/logo.png";
// 读取图片文件
try (FileInputStream imgStream = new FileInputStream(imagePath)) {
// 插入图片,参数:图片输入流、图片类型(如 "png")、宽度(单位:EMU)、高度(单位:EMU)
// 1英寸 = 1440 EMU
imageRun.addPicture(imgStream, XWPFDocument.PICTURE_TYPE_PNG, "logo.png", 3600, 1800); // 宽度2.5英寸,高度1.25英寸
} catch (Exception e) {
e.printStackTrace();
}
添加页眉和页脚
页眉和页脚是通过 XWPFHeaderFooter 对象来管理的。

// ... 创建文档之后 ...
// 1. 添加页眉
XWPFHeader header = document.createHeader();
XWPFParagraph headerPara = header.createParagraph();
XWPFRun headerRun = headerPara.createRun();
headerRun.setText("这是页眉 - 公司机密");
headerRun.setBold(true);
headerRun.setTextAlignment(ParagraphAlignment.CENTER);
// 2. 添加页脚
XWPFFooter footer = document.createFooter();
XWPFParagraph footerPara = footer.createParagraph();
XWPFRun footerRun = footerPara.createRun();
footerRun.setText("第 ");
footerRun.addTab(); // 添加一个制表符
footerRun.addBreak(); // 换行
footerRun.setText(" 页");
// 添加页码需要更复杂的操作,通常使用 `CTNum` 或 `CTSimpleField`,这里仅作示意
第四步:从模板导出(最实用的场景)
在实际项目中,我们通常不会从零开始创建 Word 文档,而是使用一个已经设计好的模板(.docx 文件),然后将动态数据(如姓名、日期、表格数据等)填充到模板的指定位置。
模板制作:
- 用 Word 制作一个
.docx文件。 - 在需要替换数据的地方,使用 或其他特殊符号作为占位符,
尊敬的 **{name}** 先生/女士:您的订单编号是 **{orderId}**。- 在表格中,可以留空,或者用
{item1},{item2}作为占位符。
Java 代码实现(使用 docx4j 或 poi 的 XWPF):
POI 本身对模板标签的解析能力有限,通常需要结合正则表达式来查找和替换文本,一个更强大和推荐的库是 docx4j,它对 OpenXML 的支持更友好,特别是对于模板引擎。
这里我们先用 POI 的方式(通过正则表达式)来实现一个简单的文本替换。
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WordTemplateExport {
public static void main(String[] args) {
// 模板路径
String templatePath = "D:/template.docx";
// 输出路径
String outputPath = "D:/output_from_template.docx";
// 数据Map
Map<String, String> dataMap = new HashMap<>();
dataMap.put("name", "王五");
dataMap.put("orderId", "ORD-20251027-001");
dataMap.put("date", "2025年10月27日");
try (FileInputStream fis = new FileInputStream(templatePath);
XWPFDocument document = new XWPFDocument(fis);
FileOutputStream out = new FileOutputStream(outputPath)) {
// 1. 替换段落中的文本
for (XWPFParagraph p : document.getParagraphs()) {
String text = p.getText();
if (text != null) {
for (Map.Entry<String, String> entry : dataMap.entrySet()) {
text = text.replace("**{" + entry.getKey() + "}**", entry.getValue());
}
// 清空段落原有内容
for (int i = 0; i < p.getRuns().size(); i++) {
p.removeRun(i);
}
// 将新文本写入段落
if (!text.isEmpty()) {
p.createRun().setText(text);
}
}
}
// 2. 替换表格中的文本 (逻辑类似段落)
for (XWPFTable table : document.getTables()) {
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
for (XWPFParagraph p : cell.getParagraphs()) {
String text = p.getText();
if (text != null) {
for (Map.Entry<String, String> entry : dataMap.entrySet()) {
text = text.replace("**{" + entry.getKey() + "}**", entry.getValue());
}
// 清空并重写
for (int i = 0; i < p.getRuns().size(); i++) {
p.removeRun(i);
}
if (!text.isEmpty()) {
p.createRun().setText(text);
}
}
}
}
}
}
document.write(out);
System.out.println("模板导出成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
总结与建议
| 功能 | POI-XWPF 实现 | 复杂度 | 备注 |
|---|---|---|---|
| 创建新文档 | new XWPFDocument() |
低 | 适用于生成简单报告。 |
| 写入文本 | XWPFParagraph.createRun().setText() |
低 | 可以设置字体、颜色、大小等。 |
| 插入表格 | document.createTable() |
低 | 操作直观,适合固定格式的数据。 |
| 插入图片 | run.addPicture() |
中 | 需要处理图片流和尺寸(EMU)。 |
| 页眉页脚 | document.createHeader/Footer() |
中 | 功能基本够用,但高级样式控制较难。 |
| **模板导出 | 正则替换文本 | 中 | 最常用场景,POI 实现较繁琐,推荐 docx4j。 |
| docx4j (推荐) | 低 | 模板引擎功能强大,支持循环、条件判断,语法类似 Freemarker。 |
最终建议:
- 对于简单的、从零开始生成的 Word 文档,使用 POI-XWPF 完全足够,并且是官方支持的选择。
- 对于需要从模板填充复杂数据(特别是表格循环、图片动态插入)的场景,强烈建议使用
docx4j库,它虽然不是 POI 的一部分,但同样是 Apache 许可的开源项目,并且专门为 Word 模板处理而生,能极大提高开发效率。
希望这份详细的指南能帮助你顺利使用 Java 导出 Word 文档!
