freemarker 生成 DOCX文档

使用 Freemarker 生成 DOCX 文档的核心思路是:将 DOCX 文件视为 ZIP 压缩包,替换其中的 XML 模板内容。以下是详细步骤和代码示例:


步骤 1:准备 DOCX 模板

  1. 创建模板 DOCX 文件
    用 Microsoft Word 或 LibreOffice 编写一个 DOCX 文件,在需要动态填充的位置插入 Freemarker 占位符(如 ${name})。

  2. 解压 DOCX 文件
    DOCX 本质是 ZIP 文件,解压后得到以下结构:

    /word/document.xml  --> 主内容文件
    /word/              --> 其他资源(图片、样式等)
    
  3. 修改 document.xml
    document.xml 中插入 Freemarker 变量(如 ${userName}),然后重新压缩为 DOCX(临时模板备用)。


步骤 2:使用 Freemarker 动态生成 XML

document.xml 作为 Freemarker 模板,动态填充数据后重新打包为 DOCX。

示例代码
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.FileUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class DocxGeneratorWithFreemarker {

    public static void main(String[] args) throws Exception {
        // 1. 数据模型
        Map<String, Object> data = new HashMap<>();
        data.put("userName", "张三");
        data.put("date", "2023-10-10");

        // 2. 生成替换后的 document.xml
        String processedXml = processTemplate(data);

        // 3. 将生成的 XML 重新打包为 DOCX
        generateDocx(processedXml, "output.docx");
    }

    /**
     * 使用 Freemarker 处理 document.xml 模板
     */
    private static String processTemplate(Map<String, Object> data) throws Exception {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
        cfg.setClassForTemplateLoading(DocxGeneratorWithFreemarker.class, "/");
        cfg.setDefaultEncoding("UTF-8");

        // 从资源目录加载 document.xml.ftl 模板
        Template template = cfg.getTemplate("document.xml.ftl");
        StringWriter writer = new StringWriter();
        template.process(data, writer);
        return writer.toString();
    }

    /**
     * 将生成的 XML 打包为 DOCX
     */
    private static void generateDocx(String xmlContent, String outputPath) throws Exception {
        // 临时解压目录(需提前准备好 DOCX 模板的其他文件)
        File tempDir = new File("path/to/unzipped_template_dir");

        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outputPath))) {
            // 遍历模板目录中的所有文件
            for (File file : FileUtils.listFiles(tempDir, null, true)) {
                String entryName = file.getAbsolutePath().replace(tempDir.getAbsolutePath() + File.separator, "")
                        .replace(File.separator, "/");

                // 如果是 document.xml,替换为动态生成的内容
                if (entryName.equals("word/document.xml")) {
                    zos.putNextEntry(new ZipEntry(entryName));
                    zos.write(xmlContent.getBytes(StandardCharsets.UTF_8));
                } else {
                    // 其他文件直接复制
                    zos.putNextEntry(new ZipEntry(entryName));
                    FileUtils.copyFile(file, zos);
                }
                zos.closeEntry();
            }
        }
    }
}

关键文件说明

  1. document.xml.ftl
    这是从 DOCX 模板中提取的 Freemarker 模板文件,内容类似:

    <w:document ...>
      <w:body>
        <w:p>
          <w:r>
            <w:t>用户名:${userName}</w:t>
          </w:r>
        </w:p>
        <w:p>
          <w:r>
            <w:t>日期:${date}</w:t>
          </w:r>
        </w:p>
      </w:body>
    </w:document>
    
  2. 模板目录结构
    解压后的 DOCX 模板需包含所有必要文件(如样式、字体等),仅替换 document.xml


注意事项

  1. 模板预处理

    • 用工具(如 Word)生成的 DOCX 可能包含复杂 XML 结构,建议先用简单模板测试。
    • 确保 XML 中的 Freemarker 变量放在 <w:t> 标签内。
  2. 依赖库

    <!-- Maven 依赖 -->
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
        <version>2.3.31</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-io</artifactId>
        <version>1.3.2</version>
    </dependency>
    
  3. 复杂格式处理

    • 表格、图片等动态内容需在 XML 中构造对应的 WordML 标签。
    • 使用 Apache POI 生成 DOCX 更简单(但需直接操作 Java API)。

替代方案:结合 Apache POI

如果需动态生成复杂 DOCX,建议 使用 Apache POI + Freemarker

// 示例:使用 POI 生成 DOCX
XWPFDocument doc = new XWPFDocument();
XWPFParagraph p = doc.createParagraph();
p.createRun().setText("Hello ${name}!");

// 结合 Freemarker 渲染内容
String xml = processFreemarkerTemplate();
doc = new XWPFDocument(new ByteArrayInputStream(xml.getBytes()));

这种方式更直接,但需熟悉 POI 的 API。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从零开始学习人工智能

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值