Java 操作 Word 的实战往往围绕文档的创建、读取以及模板替换等核心场景展开。本篇文章聚焦 Apache POI 在处理 Word 文档 (.docx) 时的关键方法,涵盖 XWPFDocument 的创建、文本段落的读取,以及基于占位符的模板替换实现。通过多段落讲解与实用代码示例,读者可以快速熟悉在企业应用中编写自动化 Word 生成和修改流程的要点。
文档创建:基于 Apache POI 的实现
创建一个空白 Word 文档
在 XWPFDocument 的帮助下,可以快速创建一个空白 Word 文档。打开输出流并写入文档数据,是确保文档能正确保存的关键步骤。
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
public class DocCreateEmpty {
public static void main(String[] args) throws IOException {
try (XWPFDocument doc = new XWPFDocument();
FileOutputStream out = new FileOutputStream("empty.docx")) {
// 直接写出文档即可
doc.write(out);
}
}
}
通过上述示例,创建空白文档的核心步骤非常清晰:实例化 XWPFDocument,然后调用 write 将内容输出到文件系统。
为段落设置样式和文本
文档中的文本通常需要通过 XWPFParagraph 与 XWPFRun 来组合并控制样式,例如粗体、字号和颜色等。
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
public class DocCreateParagraph {
public static void main(String[] args) throws Exception {
try (XWPFDocument doc = new XWPFDocument()) {
XWPFParagraph p = doc.createParagraph();
XWPFRun r = p.createRun();
r.setBold(true);
r.setFontSize(16);
r.setText("这是一个带样式的段落");
// 继续添加普通文本段落
XWPFParagraph p2 = doc.createParagraph();
XWPFRun r2 = p2.createRun();
r2.setText("第二段文本,展示多段落排版能力。");
}
}
}
在实际场景中,XWPFParagraph 控制段落层级,XWPFRun 控制文本局部样式;通过组合,可以实现丰富的排版效果,支撑企业文档模板的基本呈现。
添加表格与简单图片
除了文本,还可以在文档中插入表格以便呈现结构化数据。简单的表格创建通常涉及到 XWPFTable、Row 与 Cell 的组合。
import org.apache.poi.xwpf.usermodel.*;
public class DocCreateTable {
public static void main(String[] args) throws Exception {
try (XWPFDocument doc = new XWPFDocument();
FileOutputStream out = new FileOutputStream("table.docx")) {
XWPFTable table = doc.createTable(3, 3);
table.getRow(0).getCell(0).setText("列1");
table.getRow(0).getCell(1).setText("列2");
table.getRow(0).getCell(2).setText("列3");
// 继续填充单元格内容
doc.write(out);
}
}
}
需要在文档中嵌入图片时,通常通过 XWPFRun 的 addPicture 方法配合图片输入流实现;这一步涉及图片类型、宽高与单位等参数,需与模板的排版保持一致。
文档读取:解析 Word 内容
读取文本与段落
读取现有 Word 文档的文本,通常需要遍历 XWPFDocument 的 getParagraphs(),再对每个段落执行文本提取。
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.poi.xwpf.usermodel.*;
public class DocReadParagraphs {
public static void main(String[] args) throws IOException {
try (FileInputStream in = new FileInputStream("existing.docx");
XWPFDocument doc = new XWPFDocument(in)) {
for (XWPFParagraph para : doc.getParagraphs()) {
String text = para.getText();
System.out.println(text);
}
}
}
}
通过遍历 getParagraphs(),可以逐段提取文本内容,文本提取的核心在于对每个段落的 getText() 调用,以及对空段落的处理。此方法对快速检索和文本分析非常有用。
遍历表格与图片
除了文本,Word 文档中的表格是结构化信息的重要来源。通过 XWPFTable 可以获取行、列及单元格内容,并进行数据提取与加工。
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.poi.xwpf.usermodel.*;
public class DocReadTables {
public static void main(String[] args) throws IOException {
try (FileInputStream in = new FileInputStream("tables.docx");
XWPFDocument doc = new XWPFDocument(in)) {
for (XWPFTable table : doc.getTables()) {
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
System.out.print(cell.getText() + "\t");
}
System.out.println();
}
}
}
}
}
读取过程中,getTables() 提供了对表格集合的访问;getRows() 与 getTableCells() 则用于逐单元格读取文本,便于将表格数据导出到数据库或其他系统。
模板替换:在 Word 中实现占位符替换
使用简单占位符(${name})替换
模板替换常见做法是:在文档中预置占位符,如 ${name},读取文本后进行替换并写回文档。核心思路是对段落文本进行模式匹配与替换。
import java.util.HashMap;
import java.util.Map;
import org.apache.poi.xwpf.usermodel.*;
public class TemplateReplaceSimple {
public static void main(String[] args) throws Exception {
// 假设 doc 已经打开
XWPFDocument doc = new XWPFDocument(/* 输入流或新建文档 */);
Map values = new HashMap<>();
values.put("name", "张三");
values.put("date", "2025-08-23");
for (XWPFParagraph p : doc.getParagraphs()) {
String text = p.getText();
String replaced = text;
for (Map.Entry e : values.entrySet()) {
replaced = replaced.replace("${" + e.getKey() + "}", e.getValue());
}
if (!replaced.equals(text)) {
p.getRuns().clear();
XWPFRun run = p.createRun();
run.setText(replaced);
}
}
// 保存文档的代码省略
}
}
该实现思路简单直观:读取文本、按占位符替换、重新构建 runs,从而实现模板字段的替换效果。
基于变量的多字段替换与模板兼容性
在实际场景中,模板往往包含多字段,需要对同一个段落或跨段落的占位符进行一致替换。此时,可以将替换表设计为一个全局映射,并在遍历段落时统一应用。
import java.util.Map;
import java.util.HashMap;
import org.apache.poi.xwpf.usermodel.*;
public class TemplateReplaceBulk {
public static void main(String[] args) throws Exception {
XWPFDocument doc = new XWPFDocument(/* 输入流或新建文档 */);
Map values = new HashMap<>();
values.put("name", "李四");
values.put("department", "研发部");
values.put("date", "2025-08-23");
for (XWPFParagraph p : doc.getParagraphs()) {
String text = p.getText();
String replaced = text;
for (Map.Entry e : values.entrySet()) {
replaced = replaced.replace("${" + e.getKey() + "}", e.getValue());
}
if (!replaced.equals(text)) {
p.getRuns().clear();
XWPFRun run = p.createRun();
run.setText(replaced);
}
}
// 保存文档的代码省略
}
}
在实现时,保持占位符格式的一致性、避免跨 Run 的文本拆分以及对复杂排版的占位符定位,是提高替换稳定性的关键。
实战要点与要点要素
性能与内存管理
处理较大文档时,应关注 逐段遍历而非一次性加载整文档 的策略,以及对输出流的正确关闭。对于模板替换,避免在内存中形成过多中间文本,尽量对段落文本就地修改并及时释放资源。
// 简化演示:仅演示逐步写出,避免一次性加载整文档
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
public class DocPerformanceDemo {
public static void main(String[] args) throws Exception {
try (FileInputStream in = new FileInputStream("large.docx");
XWPFDocument doc = new XWPFDocument(in);
FileOutputStream out = new FileOutputStream("large_modified.docx")) {
// 对段落进行逐条处理,避免产生大对象
// 省略具体替换逻辑
doc.write(out);
}
}
}
在实际应用中,对段落集合的迭代、对 runs 的最小修改和及时释放资源,是保持高吞吐、可扩展性的重要因素。
兼容性与版本控制
Word 文档的兼容性更多受 POI 版本 与 Java 运行环境版本的影响。确保项目中使用的 POI 依赖 与目标 Word 文件结构相匹配,避免在旧模板上出现文本编码或样式丢失的问题。
<>
另外,模板设计 时应遵循标准样式(如标题、正文、表格标题等),以减少在不同 POI 版本之间的兼容性问题。


