Apache POI通过模板导出Word文件

Apache POI通过模板导出Word文件

精选文章moguli202025-05-21 1:45:144A+A-

使用 Apache POI 通过模板导出 Word 文件的核心思路是 在 Word 模板中预定义占位符,然后通过 POI 读取模板并动态替换内容。以下是详细步骤和完整代码示例:


完整流程

1. 准备模板(定义占位符)
       ↓
2. 加载模板(XWPFDocument)
       ↓
3. 遍历段落、表格、图片占位符
       ↓
4. 替换文本、填充表格、插入图片
       ↓
5. 保存生成的新文档


一、模板设计

在 Word 文档(.docx)中定义占位符,例如 ${name}、${tableData}、${image}:

尊敬的 ${name}:

以下是您的订单信息:

${tableData}

订单附件图片:
${image}

二、依赖配置

在 Maven 项目中添加 POI 依赖(以 poi-ooxml 为例):

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.5</version>
</dependency>

三、完整代码实现

1. 替换文本、表格和图片

import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTInd;
import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class WordTemplateExporter {

    public static void main(String[] args) throws Exception {
        // 1. 加载模板
        FileInputStream fis = new FileInputStream("template.docx");
        XWPFDocument doc = new XWPFDocument(fis);

        // 2. 定义替换数据
        Map<String, Object> data = new HashMap<>();
        data.put("${name}", "张三");
        data.put("${image}", "signature.png");

        // 动态表格数据(示例)
        List<List<String>> tableData = Arrays.asList(
            Arrays.asList("商品", "数量", "价格"),
            Arrays.asList("手机", "2", "yen5999"),
            Arrays.asList("耳机", "1", "yen299")
        );
        data.put("${tableData}", tableData);

        // 3. 执行替换
        replacePlaceholders(doc, data);

        // 4. 保存结果
        FileOutputStream fos = new FileOutputStream("output.docx");
        doc.write(fos);
        fos.close();
        fis.close();
    }

    // 核心替换方法
    private static void replacePlaceholders(XWPFDocument doc, Map<String, Object> data) throws Exception {
        for (XWPFParagraph paragraph : doc.getParagraphs()) {
            replaceTextInParagraph(paragraph, data);
        }

        // 处理表格
        for (XWPFTable table : doc.getTables()) {
            for (XWPFTableRow row : table.getRows()) {
                for (XWPFTableCell cell : row.getTableCells()) {
                    for (XWPFParagraph p : cell.getParagraphs()) {
                        replaceTextInParagraph(p, data);
                    }
                }
            }
        }

        // 处理图片
        replaceImages(doc, data);
    }

    // 替换段落中的文本
    private static void replaceTextInParagraph(XWPFParagraph paragraph, Map<String, Object> data) {
        for (XWPFRun run : paragraph.getRuns()) {
            String text = run.getText(0);
            if (text != null) {
                for (Map.Entry<String, Object> entry : data.entrySet()) {
                    if (text.contains(entry.getKey())) {
                        String value = entry.getValue().toString();
                        text = text.replace(entry.getKey(), value);
                        run.setText(text, 0);
                    }
                }
            }
        }
    }

    // 插入图片(替换占位符)
    private static void replaceImages(XWPFDocument doc, Map<String, Object> data) throws Exception {
        for (XWPFParagraph paragraph : doc.getParagraphs()) {
            for (XWPFRun run : paragraph.getRuns()) {
                String text = run.getText(0);
                if (text != null && text.contains("${image}")) {
                    // 删除占位符文本
                    run.setText("", 0);
                    // 插入图片
                    String imagePath = (String) data.get("${image}");
                    FileInputStream imageStream = new FileInputStream(imagePath);
                    run.addPicture(
                        imageStream,
                        XWPFDocument.PICTURE_TYPE_PNG,
                        "image.png",
                        Units.toEMU(200),  // 宽度(EMU单位)
                        Units.toEMU(100)   // 高度
                    );
                    imageStream.close();
                }
            }
        }
    }
}

四、关键功能说明

1. 文本替换

  • 原理:遍历所有段落和表格单元格,查找 ${key} 格式的占位符并替换。
  • 注意:避免直接修改 CTP 底层 XML,防止破坏原有样式。

2. 表格动态填充

  • 适用场景:模板中的表格需要动态填充多行数据。
  • 扩展代码:若需循环生成表格行:
  • XWPFTable table = doc.getTable(0); // 获取第一个表格 List<List<String>> data = ...; // 动态数据 for (List<String> rowData : data) { XWPFTableRow row = table.createRow(); for (int i = 0; i < rowData.size(); i++) { row.getCell(i).setText(rowData.get(i)); } }

3. 图片插入

  • 占位符匹配:通过 ${image} 标记图片位置。
  • 单位转换:使用 Units.toEMU() 将像素转换为 EMU 单位(1 EMU = 1/914400 英寸)。

五、常见问题解决

1. 中文乱码

  • 原因:未指定中文字体。
  • 解决:在运行时设置字体:
  • run.setFontFamily("宋体"); run.setFontSize(12);

2. 图片不显示

  • 检查项: 图片路径是否正确(使用绝对路径更可靠)。 图片格式是否受支持(PNG/JPG)。 占位符是否被正确替换。

3. 样式丢失

  • 原因:直接修改 CTP 导致样式重置。
  • 解决:通过 XWPFParagraph 和 XWPFRun 操作,避免直接操作底层 XML。

六、高级功能扩展

1. 保留原样式

  • 方法:在替换文本时,保留 XWPFRun 的字体、颜色等属性:
  • java
  • java
  • 复制
  • XWPFRun newRun = paragraph.insertNewRun(index); newRun.setText("新文本"); newRun.setFontFamily(run.getFontFamily()); // 继承原字体 newRun.setFontSize(run.getFontSize()); // 继承原字号

2. 动态页眉/页脚

// 修改页眉
XWPFFooter footer = doc.getFooter(0);
footer.createParagraph().createRun().setText("机密文件 - 版权所有");

// 修改页脚(含页码)
XWPFFooter footer = doc.getFooter(0);
XWPFParagraph para = footer.createParagraph();
para.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = para.createRun();
run.setText("第 " + doc.getPageNumber() + " 页");

3. 列表缩进处理

// 设置列表层级
CTNumPr numPr = pPr.getOrCreateNumPr();
CTAbstractNumId abstractNumId = numPr.getOrCreateAbstractNumId();
abstractNumId.setVal(BigInteger.valueOf(1)); // 设置列表层级

通过以上方法,可以高效实现基于模板的 Word 文件导出。对于更复杂需求(如图表、公式),建议结合 Freemarker 生成 XML 内容后导入 POI。

点击这里复制本文地址 以上内容由莫古技术网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

莫古技术网 © All Rights Reserved.  滇ICP备2024046894号-2