Java根据word模板生成word文件
1.简介
处理word的方式有许多种:
- 使用 Hutool工具类 ,但是只能处理简单的word,不能处理表格,动态图片,替换文字等
- 使用Apache POI,可以处理复杂的Word文档,但是处理过程复杂,word转xml,xml再转ftl才可进行操作,不适合经常处理word
- 其他第三方工具
- XDocReport +FreeMarker,该技术组合既简单又高效可实现word模板的编辑,docx和doc均可处理
本文将实现动态文本替换、动态图片替换、动态表格填充。
2.pom引入
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.core</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document.docx</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
3.动态文本替换
比如说我们有个邮件word模板,需要动态的在横线处填入相关信息,然后生成完整的word文档。
我们做以下操作:
- 将横线位置替换成域
- 给每个要替换的位置取个名字
- FreeMarker域格式为—— ${……}
代码实现
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
* @author: YSL
* @date: 2022/8/2 17:42
public class WordTemplate {
private static final Logger logger = LoggerFactory.getLogger(WordTemplate.class);
public static void test() {
InputStream ins = null;
OutputStream out = null;
try {
//获取Word模板,模板存放路径
ins = new FileInputStream("C:\\Users\\she52\\Desktop\\演示.docx");
//注册xdocreport实例并加载FreeMarker模板引擎
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins, TemplateEngineKind.Freemarker);
//创建xdocreport上下文对象,用于存放具体数据
IContext context = report.createContext();
//创建要替换的文本变量
context.put("name", "李梅");
context.put("sendName", "张三");
context.put("year", "2022");
context.put("month", "08");
context.put("day", "03");
//输出到本地目录
String filePath = "C:\\Users\\she52\\Desktop\\结果.docx";
out = new FileOutputStream(new File(filePath));
report.process(context, out);
} catch (Exception e) {
logger.info("生成word发生异常", e);
} finally {
try {
if (ins != null){
ins.close();
if (out != null){
out.close();
} catch (IOException e) {
logger.info("文件流关闭失败", e);
}
注意,执行代码时要把word模板关掉,否则模板加载不成功,会报错
执行结果:
4.动态表格
如果表格是标准的几行几列的列表,使用以下方法。如果不是标准的,比如说合并单元格,则需要使用上 面文本替换的方法,挨个起名赋值。
我们做以下操作:
- 除了标题行,保留一行空白行,删掉其余的空白行(为什么这么做,因为表格填充,本质是列表循环填写,列表大小是不确定的,我们只保留一行,让它自己根据列表大小创建行数)
- 起一个列表名,比如上面的示例表是用户信息表,则起名为userInfo
- 对用户信息属性挨个起名,姓名name、年龄age、性别sex
- 第2和第3步骤,等同于新建一个Java实体类
- 添加域,命名规范-----${userInfo.name}、${userInfo.age}、${userInfo.sex},自己总结规律
- 修改完后记得保存,然后关闭word
代码实现:
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
* @author: YSL
* @date: 2022/8/2 17:42
public class WordTemplate {
private static final Logger logger = LoggerFactory.getLogger(WordTemplate.class);
public static void test() {
InputStream ins = null;
OutputStream out = null;
try {
//获取Word模板,模板存放路径
ins = new FileInputStream("C:\\Users\\she52\\Desktop\\演示.docx");
//注册xdocreport实例并加载FreeMarker模板引擎
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins, TemplateEngineKind.Freemarker);
//创建xdocreport上下文对象,用于存放具体数据
IContext context = report.createContext();
//创建要替换的文本变量
List<UserInfo> userInfos = new ArrayList<>();
UserInfo userInfo = new UserInfo();
userInfo.setName("张三");
userInfo.setAge("15");
userInfo.setSex("男");
userInfos.add(userInfo);
UserInfo userInfo1 = new UserInfo();
userInfo1.setName("李四");
userInfo1.setAge("14");
userInfo1.setSex("男");
userInfos.add(userInfo1);
UserInfo userInfo2 = new UserInfo();
userInfo2.setName("李梅");
userInfo2.setAge("16");
userInfo2.setSex("女");
userInfos.add(userInfo2);
//此处的userInfo是word中命名的列表名
context.put("userInfo", userInfos);
//创建字段元数据
FieldsMetadata fm = report.createFieldsMetadata();
//Word模板中的表格数据对应的集合类型
fm.load("userInfo", UserInfo.class, true);
//输出到本地目录
String filePath = "C:\\Users\\she52\\Desktop\\结果.docx";
out = new FileOutputStream(new File(filePath));
report.process(context, out);
} catch (Exception e) {
logger.info("生成word发生异常", e);
}finally {
try {
if (ins != null){
ins.close();
if (out != null){
out.close();
} catch (IOException e) {
logger.info("文件流关闭失败", e);
@Data
public class UserInfo{
private String name;
private String age;
private String sex;
}
执行结果
注意,代码中创建的UserInfo实体类必须是public公共的,否则赋不到值
5.动态图片
比如说word文档中有,勾选框, 同意则打对勾。我们可以将勾选框变成图片,再令准备一张打好对勾的图片,如果勾选则将其替换,否则不替换,就可以完美解决勾选问题。
做以下操作:
- 将勾选的圆圈替换成大小相同的图片,可以让UI帮忙整一个
- 给图片添加标签并命名,直接写名字就行,不用加${}
代码实现:
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.images.ByteArrayImageProvider;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
* @author: YSL
* @date: 2022/8/2 17:42
public class WordTemplate {
private static final Logger logger = LoggerFactory.getLogger(WordTemplate.class);
public static void test() {
InputStream in = null;
OutputStream out = null;
FileInputStream fins = null;
try {
//获取Word模板,模板存放路径
in = new FileInputStream("C:\\Users\\she52\\Desktop\\演示.docx");
//注册xdocreport实例并加载FreeMarker模板引擎
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(in, TemplateEngineKind.Freemarker);
//创建xdocreport上下文对象,用于存放具体数据
IContext context = report.createContext();
//图片元数据
FieldsMetadata metadata = report.createFieldsMetadata();
//读取对勾圆形图片
fins = new FileInputStream(new File("C:\Users\she52\Desktop\hookRound.png"));
metadata.addFieldAsImage("accept");
//只在同意上打对勾,不同意那个图片则不用管
context.put("accept", new ByteArrayImageProvider(fins));
report.setFieldsMetadata(metadata);
//输出到本地目录
String filePath = "C:\\Users\\she52\\Desktop\\结果.docx";
out = new FileOutputStream(new File(filePath));
report.process(context, out);
} catch (Exception e) {
logger.info("生成word发生异常", e);
}finally {
try {
if (in != null){
in.close();
if(fins != null){
fins.close();
if(out != null){
out.close();
} catch (IOException e) {
logger.info("文件流关闭失败",e);