1. 准备工作:添加 docx4j 依赖
在 pom.xml 中加入(以 8.2.9 版本为例,支持 JAXB 自带实现,避免 JDK 9+ 的模块问题):
xml
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j</artifactId>
<version>8.2.9</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
若使用 Spring Boot 项目且 JDK 版本较高,docx4j 8.2.9 会自动引入合适的 JAXB 实现,通常只需上面两个依赖。
2. 核心代码:创建一个带标记和标题的内容控件
下面的方法会创建一个新的空白 Word 文档,并在其中插入一个格式文本内容控件 (SdtBlock) ,同时设置 Tag 和 Title(代码中通过 SdtPr.Alias 对应 Word 的标题字段)。
java
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import java.io.File;
public class CreateSdtBlockExample {
public static void main(String[] args) throws Exception {
// 1. 创建空白 docx 包
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();
// 2. 创建一个格式文本内容控件 SdtBlock
SdtBlock sdtBlock = createRichTextSdtBlock("REF_QUAL_001", "资格条件内容区域");
// 3. 将控件添加到文档主体
mainDocumentPart.getContent().add(sdtBlock);
// 4. 保存文件
wordMLPackage.save(new File("output_with_sdt.docx"));
System.out.println("文档已生成:output_with_sdt.docx");
}
/**
* 构建一个带 Tag 和 Title(Alias)的富文本内容控件
* @param tagValue 标记(对应 Word 属性中的"标记")
* @param titleValue 标题(对应 Word 属性中的"标题")
* @return SdtBlock 对象
*/
private static SdtBlock createRichTextSdtBlock(String tagValue, String titleValue) {
ObjectFactory factory = new ObjectFactory();
// -------- 构建控件属性 <w:sdtPr> --------
SdtPr sdtPr = factory.createSdtPr();
// 设置 Tag(<w:tag w:val="..."/>)
Tag tag = factory.createTag();
tag.setVal(tagValue);
sdtPr.getRPrOrAliasOrLock().add(tag);
// 设置 Title,在 docx4j 中用 SdtPr.Alias 对应 <w:alias w:val="..."/>
SdtPr.Alias alias = factory.createSdtPrAlias();
alias.setVal(titleValue);
sdtPr.getRPrOrAliasOrLock().add(alias);
// 可选:设置控件为富文本类型(place holder 等),这里保持默认即可
// -------- 构建控件内容 <w:sdtContent> --------
SdtContentBlock sdtContent = factory.createSdtContentBlock();
// 在控件内放一个占位段落
P paragraph = factory.createP();
R run = factory.createR();
Text text = factory.createText();
text.setValue("请在此输入内容...");
run.getContent().add(text);
paragraph.getContent().add(run);
sdtContent.getContent().add(paragraph);
// -------- 组装 SdtBlock --------
SdtBlock sdtBlock = factory.createSdtBlock();
sdtBlock.setSdtPr(sdtPr);
sdtBlock.setSdtContent(sdtContent);
return sdtBlock;
}
}
代码对应关系
| Word 界面操作 | docx4j 代码实现 |
|---|---|
| 开发工具 → 格式文本内容控件 | 创建 SdtBlock 对象 |
| 选中控件 → 属性 → 标记(Tag) | Tag tag = factory.createTag(); tag.setVal("ID"); 放入 SdtPr |
| 选中控件 → 属性 → 标题(Title) | SdtPr.Alias alias = factory.createSdtPrAlias(); alias.setVal("标题"); 放入 SdtPr |
| 控件内的提示文字 | 在 SdtContentBlock 中添加一个 P(段落),内含 R 和 Text |
3. 向现有文档追加内容控件
如果你的场景是基于已有模板动态插入控件,只需加载现有文件,然后添加到 mainDocumentPart.getContent() 即可:
java
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File("template.docx"));
MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart();
SdtBlock newSdt = createRichTextSdtBlock("REF_EVAL_005", "评标办法区");
mainPart.getContent().add(newSdt); // 追加到末尾(或按需插入指定位置)
wordMLPackage.save(new File("template_with_sdt.docx"));
4. 两个容易混淆的属性说明
- Tag(标记) :在 OOXML 中对应
<w:tag w:val="xxx"/>,是程序识别控件的主要标识,你的SdtBlockUtils正是通过它来查找控件。 - Title(标题) :在 OOXML 中对应
<w:alias w:val="xxx"/>(docx4j 中用SdtPr.Alias表示),也就是 Word 属性对话框中的"标题"输入框。它更多用于界面提示,也可作为备用标识。
代码中
getSdtBlockAlias读取的就是这个Alias值。
5. 注意事项
- JAXB 兼容性 :如果运行时报 JAXB 相关错误,请检查依赖是否完整。使用 docx4j 8.2.9 以上版本一般可以自动处理,但你也可尝试
org.docx4j:docx4j-JAXB-ReferenceImpl等预绑定包。 - 控件的默认样式 :新创建的 SdtBlock 使用的字体、缩进等会继承文档默认样式,你可以通过添加
PPr/RPr进一步设置。 - 生产环境建议:你的示例工具类中已经有很好的异常降级机制,这部分创建控件的逻辑也可以整合进去,作为生成模板模块的一部分。
这样,就完全用 Java 实现了"给 Word 补充内容控件并设置标记和标题"的全过程。生成的 docx 文件再用 SdtBlockUtils 去填充内容,就形成了一套完整的自动化文档流水线。