docx模板生成docx文档——踩坑经验

为什么使用docx

​ office 2007以后,docx以逐步替代doc文件,docx文件比doc文件所占用空间更小,其本质上是一个XML文件。故Apache开发的POI围绕其打造了一系列的处理工具。本次需求使用的类以XWPF开头。包括XWPFDocument、XWPFParagraph、XWPFRun……

XWPF(XML字处理器格式):用于读取和写入MS-Word的扩展文件 .docx ,适用office 2007以后版本
HWPF:用于读取和写入MS-Word的.doc扩展文件,适用office 2007以前版本

占位符${……}替换文本,构建模板

存在问题:docx格式下编辑占位符时,可能出现${……}没法形成整体,导致代码获取run时占位符不完整,无法识别替换
解决方法:先把docx格式另存为xml格式,并把完整的${……}从别处复制到目标位置上。保险起见可以用vscode或其他xml软件预览以确定${……}是否是完整连续的。

调用模板插入文本——代码实现

//获取模板 (OPC是一个文件容器技术。被微软创建,用来存储XML。)
OPCPackage opcPackage = POIXMLDocument.openPackage(templatePath);

//读取文件(XWPFDocument类简化xml的底层结构,方便操作,但还不是个稳定成熟的api)
//存在问题,sonarlint可能会要求把该行代码放进try(……){}里,实现运行完后释放。但实际上try结束后执行的是.close()方法,导致缓存中已被替换的文本会写入模板文件并覆盖掉模板文本,从而使模板丢失。
//解决方式,不放入try(……)中,没有close的必要性。
XWPFDocument doc = new XWPFDocument(opcPackage);
	

//获取段落
List<XWPFParagraph> paragraphList = doc.getParagraphs();

//获取run.在Word文档中段落的最小的操作单位是XWPFRun,正常的一个段落,会被分割成多个小的XWPFRun,这些XWPFRun组合在一起就是一个完整的段落。
List<XWPFRun> runs = par.getRuns();
	

/*
	操作文本
	run.xxx();
*/

//替换占位符
String text = run.getText();
……
text = text.replace("${……}", value);	
run.setText(text, pos); //pos为插入run中的位置

//把处理完的doc写入到新文件里
FileOutputStream fileOutputStream = new FileOutputStream(file);
doc.write(fileOutputStream);	
		

此处可以关闭文件输出流,但不要doc.close(),会导致修改完的文本回填覆盖掉模板文件。
来看官方解释这个close方法:

​ XWPFDocument.close()

​ Closes the underlying OPCPackage from which this document was read, if there is one.

​ "关闭底层的被读取的OPCPackage文件 "

再看OPCPackage.close()

​ Close the open, writable package and save its content. If your package is open read only, then you should call revert() when finished with the package. This method is not thread-safe.

​ “关闭这个打开的、可写入的包,并保存它的文本内容。如果这个包打开后只进行读取,结束使用这个包后应该调用revert()方法。这个方法是线程不安全的。”

附带看OPCPackage.revert()

​ Close the package WITHOUT saving its content. Reinitialize this package and cancel all changes done to it.

​ “关闭包而不保存它的内容。初始化这个包并取消所有的更改。”

由此可见把打开docx的动作放进try(……)里是不合理的,会导致覆盖发生。若需要关闭读取文件,需使用opcPackage.revert()。

可以使用Apache POI和FreeMarker来实现Java生成docx文档的功能。以下是一个简单的示例,演示如何使用这两个库来生成docx文档。 1. 首先,你需要在项目中导入Apache POI和FreeMarker的依赖。这可以在pom.xml文件中完成,如下所示: ``` <dependencies> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> </dependencies> ``` 2. 创建一个docx模板文件。你可以使用Microsoft Word等工具来创建模板文件,并将其保存为docx格式。 3. 在Java中编写代码来读取模板文件,并将数据填充到模板中。以下是一个示例代码: ```java import java.io.*; import java.util.*; import org.apache.poi.xwpf.usermodel.*; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.util.Units; import freemarker.template.Configuration; import freemarker.template.Template; public class DocxGenerator { public static void main(String[] args) { Map<String, Object> data = new HashMap<>(); data.put("name", "John Doe"); data.put("age", 30); data.put("address", "123 Main St."); try { // Read the template file InputStream is = new FileInputStream("template.docx"); XWPFDocument doc = new XWPFDocument(is); // Use FreeMarker to populate the template with data Configuration cfg = new Configuration(Configuration.VERSION_2_3_30); cfg.setClassForTemplateLoading(DocxGenerator.class, "/"); Template template = cfg.getTemplate("template.ftl"); StringWriter writer = new StringWriter(); template.process(data, writer); // Replace the placeholder in the document with the generated content for (XWPFParagraph p : doc.getParagraphs()) { List<XWPFRun> runs = p.getRuns(); if (runs != null) { for (XWPFRun r : runs) { String text = r.getText(0); if (text != null && text.contains("{{content}}")) { text = text.replace("{{content}}", writer.toString()); r.setText(text, 0); } } } } // Save the document to a file OutputStream os = new FileOutputStream("output.docx"); doc.write(os); os.close(); doc.close(); } catch (IOException e) { e.printStackTrace(); } catch (InvalidFormatException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } ``` 在这个示例中,我们首先从模板文件中读取docx文档,并使用FreeMarker将数据填充到模板中。然后,我们遍历文档中的段落和运行,并查找包含“{{content}}”的文本,并将其替换为生成的内容。最后,我们将生成文档保存到一个新文件中。 4. 创建一个FreeMarker模板文件,以指定如何填充数据。以下是一个示例模板文件: ``` Name: {{name}} Age: {{age}} Address: {{address}} ``` 在这个模板中,我们使用“{{name}}”、“{{age}}”和“{{address}}”作为占位符,以指定生成文档中的数据位置。 5. 运行Java代码,生成docx文档。 这是一个简单的示例,演示了如何使用Java生成docx文档。你可以根据自己的需求修改代码和模板文件,以生成更复杂的文档
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值