基础准备:添加 POI 依赖
你需要在你的项目中添加 Apache POI 的相关依赖,由于 POI 的不同模块(如 Excel, Word, PowerPoint)是分开的,我们只需要引入 Word 相关的即可。

对于 Maven 项目,在 pom.xml 文件中添加以下依赖:
<dependencies>
<!-- Apache POI 核心依赖 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.4</version> <!-- 建议使用较新版本 -->
</dependency>
<!-- Apache POI OOXML 依赖,用于处理 .docx 格式 -->
<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>
对于 Gradle 项目,在 build.gradle 文件中添加:
dependencies {
implementation 'org.apache.poi:poi:5.2.4'
implementation 'org.apache.poi:poi-ooxml:5.2.4'
}
POI 操作 Word 的核心概念
Apache POI 操作 Word (.docx 文件) 主要使用 XWPF (XML Word Format Processing) 包,你需要了解几个核心类:
XWPFDocument: 代表一个 Word 文档,它是所有操作的起点和容器。XWPFParagraph: 代表 Word 文档中的一个段落。XWPFRun: 代表段落中具有相同格式样式的一段连续文本,一个段落可以包含多个Run,每个Run可以有不同的字体、颜色、大小等样式。XWPFTable: 代表一个表格。XWPFTableRow: 代表表格中的一行。XWPFTableCell: 代表表格中的一个单元格。
简单关系: XWPFDocument (文档) -> XWPFParagraph (段落) -> XWPFRun (文本块) 或 XWPFTable (表格) -> XWPFTableRow (行) -> XWPFTableCell (单元格)。

生成一个简单的 Word 文档
下面是一个最基础的例子,创建一个包含标题和正文的 Word 文档。
import org.apache.poi.xwpf.usermodel.*;
import java.io.FileOutputStream;
import java.io.IOException;
public class SimpleWordGenerator {
public static void main(String[] args) {
// 1. 创建一个空的 XWPFDocument 对象
XWPFDocument document = new XWPFDocument();
try (FileOutputStream out = new FileOutputStream("D:/simple_document.docx")) {
// 2. 创建一个段落
XWPFParagraph titleParagraph = document.createParagraph();
XWPFRun titleRun = titleParagraph.createRun();
// 3. 设置段落内容
titleRun.setText("这是我的第一个 POI Word 文档标题");
titleRun.setBold(true); // 设置为粗体
titleRun.setFontSize(16); // 设置字体大小
titleRun.setFontFamily("宋体"); // 设置字体
// 4. 创建第二个段落(正文)
XWPFParagraph bodyParagraph = document.createParagraph();
XWPFRun bodyRun = bodyParagraph.createRun();
bodyRun.setText("这是一个段落,POI 是一个非常强大的 Java 库,可以用来操作 Office 文档。");
bodyRun.setFontSize(12);
bodyRun.setFontFamily("宋体");
// 5. 将文档写入到输出流
document.write(out);
System.out.println("Word 文档生成成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 6. 关闭文档
try {
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行这段代码后,你会在 D: 盘根目录下找到一个名为 simple_document.docx 的文件。
高级功能示例
下面我们来看一些更复杂的功能,包括列表、表格、图片和页眉页脚。
1 添加列表
// 在 main 方法中,在正文段落之后添加以下代码
XWPFParagraph listParagraph = document.createParagraph();
// 设置列表类型为项目符号列表
listParagraph.setNumID(1); // 注意:这里的 1 是一个预设的列表 ID,需要预先定义
XWPFRun listRun1 = listParagraph.createRun();
listRun1.setText("列表项一");
listRun1.setFontFamily("宋体");
XWPFRun listRun2 = listParagraph.createRun();
listRun2.setText("列表项二");
listRun2.setFontFamily("宋体");
注意: 直接设置 setNumID 可能需要你预先在文档中定义好列表样式,更灵活的方式是使用 CTNumPr 和 CTAbstractNum 来动态定义,但这比较复杂,对于简单应用,直接用段落和缩进模拟列表也是可行的。

2 添加表格
// 在 main 方法中添加
// 创建一个 3x3 的表格
XWPFTable table = document.createTable(3, 3);
// 获取第一行
XWPFTableRow row1 = table.getRow(0);
// 设置第一行的单元格
row1.getCell(0).setText("姓名");
row1.getCell(1).setText("年龄");
row1.getCell(2).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("25");
row3.getCell(2).setText("上海");
// 可以设置表格样式,例如合并单元格
// table.getRow(0).getCell(0).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
// table.getRow(1).getCell(0).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
3 插入图片
// 在 main 方法中添加
XWPFParagraph imageParagraph = document.createParagraph();
XWPFRun imageRun = imageParagraph.createRun();
try {
// 读取图片文件
String imagePath = "D:/path/to/your/image.png"; // 替换为你的图片路径
// 图片宽度(单位:twip, 1 cm = 567 twip)
int width = 300 * 100; // 300像素
// 图片高度(单位:twip)
int height = 200 * 100; // 200像素
// 插入图片
imageRun.addPicture(new FileInputStream(imagePath), XWPFDocument.PICTURE_TYPE_PNG, imagePath, Units.toEMU(width), Units.toEMU(height));
imageRun.addBreak(); // 换行
} catch (Exception e) {
e.printStackTrace();
}
注意: 你需要 import org.apache.poi.util.Units;。
4 添加页眉和页脚
// 在 main 方法中,在 document.write(out) 之前添加
// 添加页眉
XWPFHeader header = document.createHeader(HeaderFooterType.DEFAULT);
XWPFParagraph headerPara = header.createParagraph();
XWPFRun headerRun = headerPara.createRun();
headerRun.setText("这是页眉内容");
headerRun.setBold(true);
// 添加页脚
XWPFFooter footer = document.createFooter(HeaderFooterType.DEFAULT);
XWPFParagraph footerPara = footer.createParagraph();
XWPFRun footerRun = footerPara.createRun();
footerRun.setText("第 1 页"); // POI 对页码的动态支持较弱,通常需要手动处理或使用更复杂的库
完整的高级示例代码
下面是一个结合了上述所有功能的完整示例。
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTNumPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class AdvancedWordGenerator {
public static void main(String[] args) {
// 1. 创建文档
XWPFDocument document = new XWPFDocument();
// 2. 添加标题
XWPFParagraph title = document.createParagraph();
XWPFRun titleRun = title.createRun();
titleRun.setText("高级 POI Word 文档示例");
titleRun.setBold(true);
titleRun.setFontSize(20);
titleRun.setFontFamily("微软雅黑");
titleRun.addBreak(); // 换行
// 3. 添加正文段落
XWPFParagraph para1 = document.createParagraph();
XWPFRun para1Run = para1.createRun();
para1Run.setText("这是一个包含多种元素的段落,下面将展示列表、表格和图片。");
para1Run.setFontSize(12);
para1Run.setFontFamily("宋体");
// 4. 添加列表(简化版,通过缩进模拟)
XWPFParagraph listPara1 = document.createParagraph();
listPara1.setIndentationLeft(500); // 设置左缩进
XWPFRun listRun1 = listPara1.createRun();
listRun1.setText("这是列表项 1");
listRun1.setFontFamily("宋体");
XWPFParagraph listPara2 = document.createParagraph();
listPara2.setIndentationLeft(500);
XWPFRun listRun2 = listPara2.createRun();
listRun2.setText("这是列表项 2");
listRun2.setFontFamily("宋体");
// 5. 添加表格
XWPFTable table = document.createTable(3, 3);
table.setWidth("100%"); // 设置表格宽度为页面宽度
// 设置表格样式(可选)
// table.setTableAlignment(TableRowAlign.CENTER);
// 填充表格数据
String[][] tableData = {
{"姓名", "年龄", "职业"},
{"王五", "28", "软件工程师"},
{"赵六", "32", "产品经理"}
};
for (int i = 0; i < table.getNumberOfRows(); i++) {
XWPFTableRow row = table.getRow(i);
for (int j = 0; j < table.getNumberOfColumns(); j++) {
XWPFTableCell cell = row.getCell(j);
cell.setText(tableData[i][j]);
// 设置单元格内容居中
cell.getCTTc().getTcPr().addNewJc().setVal(org.openxmlformats.schemas.wordprocessingml.x2006.STJc.CENTER);
}
}
// 6. 添加图片(请确保图片路径存在)
XWPFParagraph imagePara = document.createParagraph();
imagePara.setAlignment(ParagraphAlignment.CENTER); // 图片居中
XWPFRun imageRun = imagePara.createRun();
try {
String imagePath = "D:/logo.png"; // 替换为你的图片路径
imageRun.addPicture(new FileInputStream(imagePath), XWPFDocument.PICTURE_TYPE_PNG, imagePath, Units.toEMU(300), Units.toEMU(150));
} catch (Exception e) {
System.err.println("图片路径错误或文件不存在,已跳过图片插入。");
e.printStackTrace();
}
// 7. 添加页眉和页脚
XWPFHeader header = document.createHeader(HeaderFooterType.DEFAULT);
XWPFParagraph headerPara = header.createParagraph();
XWPFRun headerRun = headerPara.createRun();
headerRun.setText("公司机密 - 请勿外传");
headerRun.setItalic(true);
XWPFFooter footer = document.createFooter(HeaderFooterType.DEFAULT);
XWPFParagraph footerPara = footer.createParagraph();
XWPFRun footerRun = footerPara.createRun();
footerRun.setText("© 2025 My Company");
footerRun.setSubscript(VerticalAlign.SUPERSCRIPT); // 设置为上标
// 8. 保存文档
try (FileOutputStream out = new FileOutputStream("D:/advanced_document.docx")) {
document.write(out);
System.out.println("高级 Word 文档生成成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
重要注意事项与替代方案
1 POI 的局限性
.doc格式: POI 对旧版 Word 文档(.doc)的支持非常有限且不稳定。强烈建议只使用.docx格式。- 样式复杂度: 对于非常复杂的样式(如复杂的页眉页脚、图文混排、样式继承等),POI 的 API 会变得非常繁琐,难以控制。
- 模板处理: 直接用代码生成所有内容很灵活,但当文档结构非常复杂时,代码会变得臃肿且难以维护,更常见的做法是使用模板。
2 使用模板(推荐方案)
对于生产环境,最佳实践是使用 Word 模板。
- 创建模板: 在 Word 中设计好你的文档框架,包括固定的文字、Logo、表格结构、页眉页脚等,将需要动态替换的内容(如姓名、日期)用
${name},${date}这样的占位符标记。 - 使用 POI 操作模板: 使用 POI 打开这个
.docx模板文件。 - 替换占位符: 遍历文档中的所有段落和文本块,找到 形式的占位符,并用你的实际数据替换它。
- 填充表格: 找到模板中的表格,然后根据数据动态添加行。
- 保存结果: 将处理后的文档另存为一个新的文件。
这种方法将样式设计和数据逻辑分离,大大提高了开发效率和文档的可维护性。
3 其他强大的替代库
POI 无法满足你的需求,可以考虑以下库:
- docx4j: 一个功能非常强大的 Java 库,专门用于处理 OpenXML 格式(
.docx,.xlsx,.pptx),它的 API 比 POI 更面向对象,对复杂样式的支持更好。 - iText: 虽然 iText 以 PDF 处理闻名,但它也有一个
iText for Word(iText 5) 的版本,功能也很强大,iText 5 的商业使用需要付费许可证,iText 7 的 Word 处理模块是商业产品。
| 功能 | 核心类/方法 | 描述 |
|---|---|---|
| 创建文档 | new XWPFDocument() |
创建一个空的 Word 文档对象。 |
| 创建段落 | document.createParagraph() |
在文档中添加一个新段落。 |
| 创建文本块 | paragraph.createRun() |
在段落中创建一个具有相同样式的文本块。 |
| 设置文本 | run.setText("内容") |
为文本块设置内容。 |
| 设置样式 | run.setBold(true), run.setFontSize(12) |
设置文本的粗体、字体大小等样式。 |
| 创建表格 | document.createTable(rows, cols) |
创建一个指定行数和列数的表格。 |
| 操作单元格 | row.getCell(col).setText("内容") |
获取指定单元格并设置内容。 |
| 插入图片 | run.addPicture(...) |
在文本块中插入图片,需要指定图片路径、类型和尺寸。 |
| 添加页眉/页脚 | document.createHeader/Footer(...) |
为文档添加页眉或页脚。 |
希望这份详细的指南能帮助你顺利地使用 Java POI 生成 Word 文档!从简单的例子开始,逐步尝试更复杂的功能,你会很快掌握它。
