Spring Boot中如何向Word文档段落动态填充数据并保留原模板格式
Spring Boot中如何向Word文档段落动态填充数据并保留原模板格式
你说的这个情况我太有共鸣了!之前帮同事调过类似的需求,要么填完数据后字体、行距全乱套,要么格式保住了但数据死活插不对位置,尤其是带自定义样式、图片的复杂模板,简直让人头大。下面给你分享几个亲测有效的解决思路,都是实打实踩坑踩出来的:
一、选对工具库是关键
Java生态里处理Word文档,常用的就是Apache POI和Docx4j,这俩我都用过,给你说说各自的用法和优势:
1. Apache POI:轻量灵活,适合简单模板
POI是很多人最先接触的库,但要注意不能用简单的字符串替换整个段落——那样会直接把段落的格式(缩进、行距、字体)全部清空。正确的姿势是操作到「Run」级别,也就是Word里自带格式的最小文本块:
- 先在模板里给要替换的内容设占位符,比如
${userName}、${orderNo},而且要保证占位符单独在一个Run里(别和其他文本混在一起,不然替换时会带乱格式) - 加载模板后,遍历每个段落,再遍历段落里的每个Run,判断Run的文本里是否包含占位符,直接替换Run里的文本,这样Run的格式(字体大小、颜色、加粗)会完全保留
给你贴个我之前写的简化版代码:
// 加载本地模板文档,也可以从资源文件里读 XWPFDocument doc = new XWPFDocument(new FileInputStream("template.docx")); // 遍历文档中所有段落 for (XWPFParagraph para : doc.getParagraphs()) { // 遍历段落中的每个带格式的文本块(Run) for (XWPFRun run : para.getRuns()) { String runText = run.getText(0); if (runText != null) { // 替换占位符,第二个参数0表示覆盖原文本,保留Run格式 if (runText.contains("${userName}")) { run.setText(runText.replace("${userName}", "张三"), 0); } if (runText.contains("${orderNo}")) { run.setText(runText.replace("${orderNo}", "ORD20240520001"), 0); } } } } // 输出到文件,Spring Boot里可以直接写到response的输出流里供下载 doc.write(new FileOutputStream("filled_template.docx")); doc.close();
2. Docx4j:更专业的OpenXML操作,复杂模板首选
如果你的模板里有表格、内容控件、图片这类复杂元素,Docx4j比POI更靠谱,它是完全基于Word的OpenXML标准来操作的,替换数据时几乎不会破坏原格式:
- 模板里同样用
${变量名}当占位符,先打开模板,用VariablePrepare.prepare(docx)预处理文档 - 然后把要替换的键值对放进一个Map里,直接调用
MainDocumentPart.variableReplace(map)就能完成替换,全程会保留原有的所有格式,包括表格的边框、单元格样式、图片位置
举个简单的代码例子:
// 加载模板 WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File("template.docx")); MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart(); // 预处理模板,确保变量能被正确识别 VariablePrepare.prepare(wordMLPackage); // 准备要替换的变量 Map<String, String> variables = new HashMap<>(); variables.put("userName", "张三"); variables.put("orderNo", "ORD20240520001"); // 执行替换,保留原格式 mainPart.variableReplace(variables); // 输出文档 wordMLPackage.save(new File("filled_template.docx"));
二、避坑小Tips
- 模板里的占位符一定要单独占一个Run:你可以打开Word的「开始」选项卡,点击「显示/隐藏编辑标记」,就能看到每个Run的边界,确保
${userName}是一个独立的标记块 - 不要用字符串替换整个文档的XML内容:这种操作看似简单,但很容易破坏Word的内部XML结构,导致文档打不开或者格式全乱
- 如果是Spring Boot项目,要注意把模板放在
resources目录下,读取时用ResourceLoader来加载,避免路径问题
内容来源于stack exchange




