广告

Java操作Word实战教程:基于Apache POI的文档创建、读取与模板替换

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 将内容输出到文件系统。

为段落设置样式和文本

文档中的文本通常需要通过 XWPFParagraphXWPFRun 来组合并控制样式,例如粗体、字号和颜色等。

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 控制文本局部样式;通过组合,可以实现丰富的排版效果,支撑企业文档模板的基本呈现。

添加表格与简单图片

除了文本,还可以在文档中插入表格以便呈现结构化数据。简单的表格创建通常涉及到 XWPFTableRowCell 的组合。

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);
        }
    }
}

需要在文档中嵌入图片时,通常通过 XWPFRunaddPicture 方法配合图片输入流实现;这一步涉及图片类型、宽高与单位等参数,需与模板的排版保持一致。

文档读取:解析 Word 内容

读取文本与段落

读取现有 Word 文档的文本,通常需要遍历 XWPFDocumentgetParagraphs(),再对每个段落执行文本提取。

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 版本之间的兼容性问题。

广告

后端开发标签