前言
最近有个java填充word模板的需求,包括文本,列表和复选框勾选,写一个工具类,以此记录。
提示:以下是本篇文章正文内容,下面案例可供参考
一、设置word模板
选择文档中要填充的地方点击->选择插入->文档部件->域->域名(mergefeild)->填写变量名称.
普通字段


填充完毕:

列表字段
操作和普通字段一样,区别是需要在首行第一列插入列表开始域,首行最后一列插入结束域,中间正常字段。格式为:starttable:<数组字段名>,endtable:<数组字段名>

复选框
复选框找了好多种方法尝试没有成功,最后取巧,和普通字段一样设置占位符,通过代码逻辑处理.

二、代码
1. 引入pom
poi/hutool/aspose-words/gson:
<!-- hutool工具类 -->
<dependency>
<groupid>cn.hutool</groupid>
<artifactid>hutool-all</artifactid>
<version>5.7.14</version>
</dependency>
<!--word模板数据解析-->
<dependency>
<groupid>com.deepoove</groupid>
<artifactid>poi-tl</artifactid>
<version>1.9.0-beta</version>
</dependency>
<!-- word/pdf操作 -->
<dependency>
<groupid>com.aspose</groupid>
<artifactid>aspose-words</artifactid>
<version>18.8</version>
</dependency>
<dependency>
<groupid>com.google.code.gson</groupid>
<artifactid>gson</artifactid>
<version>2.8.9</version>
</dependency>
2. 模板放入项目
这里是放在项目里,也可以放在云上存储。

破解文件放入resouce,否则会有水印,aspose-words在maven仓库中也没有,需要下载后安装在本地仓库。
安装命令
mvn install:install-file -dfile=路径/aspose-words-18.8.jar -dgroupid=com.aspose -dartifactid=aspose-words -dversion=18.8 -dpackaging=jar
引入破解文件

<license>
<data>
<products>
<product>aspose.total for java</product>
<product>aspose.words for java</product>
</products>
<editiontype>enterprise</editiontype>
<subscriptionexpiry>20991231</subscriptionexpiry>
<licenseexpiry>20991231</licenseexpiry>
<serialnumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</serialnumber>
</data>
<signature>snllkgmudf0r8o1kkilwagdgfs2bvjb/2xp8p5iudvfzxmhppo+d0ran1p9tkdjv4abwagkxxj3jcqtqe/2irfqwnpf8itn8afzlv3tjpyed3ywe7it55gz6eijupc7akeoohtb4w2fpox58wwof3snp6sk6jdfiaugehyj9pju=</signature>
</license>
3.代码
实体类
import lombok.allargsconstructor;
import lombok.data;
import lombok.noargsconstructor;
import java.io.serializable;
import java.util.arrays;
import java.util.list;
@data
@allargsconstructor
@noargsconstructor
public class fillworddto implements serializable {
private string name;
private string age;
private string yuyan;
private string yingyu;
private string deyu;
private string fayu;
private string zhengshu;
private string yiji;
private string erji;
private list<experiencelist> experiencelist;
public static fillworddto create(){
fillworddto fillworddto = new fillworddto();
fillworddto.setname("小王");
fillworddto.setage("18");
fillworddto.setyuyan("☑");
fillworddto.setyingyu("☑");
fillworddto.setdeyu("□");
fillworddto.setfayu("☑");
fillworddto.setzhengshu("☑");
fillworddto.setyiji("☑");
fillworddto.seterji("□");
fillworddto.setexperiencelist(arrays.aslist(
new experiencelist("小王", "2020-01-01", "2020-01-01", "小王"),
new experiencelist("小王", "2020-01-01", "2020-01-01", "小王")
));
return fillworddto;
}
}
@data
@allargsconstructor
@noargsconstructor
class experiencelist {
private string school;
private string starttime;
private string endtime;
private string remark;
}
工具类
import com.aspose.words.*;
import com.aspose.words.net.system.data.datarow;
import com.aspose.words.net.system.data.datatable;
import java.awt.*;
import java.awt.geom.affinetransform;
import java.awt.image.bufferedimage;
import java.awt.image.colormodel;
import java.awt.image.writableraster;
import java.beans.propertydescriptor;
import java.io.*;
import java.lang.reflect.field;
import java.lang.reflect.method;
import java.util.hashmap;
import java.util.list;
import java.util.map;
public class contractutil {
private contractutil() {
}
/**
* 调整bufferedimage大小
* @param source bufferedimage 原始image
* @param targetw int 目标宽
* @param targeth int 目标高
* @param flag boolean 是否同比例调整
* @return bufferedimage 返回新image
*/
public static bufferedimage resizebufferedimage(bufferedimage source, int targetw, int targeth, boolean flag) {
int type = source.gettype();
bufferedimage target = null;
double sx = (double) targetw / source.getwidth();
double sy = (double) targeth / source.getheight();
if (flag && sx > sy) {
sx = sy;
targetw = (int) (sx * source.getwidth());
} else if(flag && sx <= sy){
sy = sx;
targeth = (int) (sy * source.getheight());
}
if (type == bufferedimage.type_custom) { // handmade
colormodel cm = source.getcolormodel();
writableraster raster = cm.createcompatiblewritableraster(targetw, targeth);
boolean alphapremultiplied = cm.isalphapremultiplied();
target = new bufferedimage(cm, raster, alphapremultiplied, null);
} else {
target = new bufferedimage(targetw, targeth, type);
}
graphics2d g = target.creategraphics();
g.setrenderinghint(renderinghints.key_rendering, renderinghints.value_render_quality);
g.drawrenderedimage(source, affinetransform.getscaleinstance(sx, sy));
g.dispose();
return target;
}
/**
* 填充 word 模板(object数据格式)
*
* @param modelwordbyte word模版二进制文件
* @param obj 要填充的数据
* @return 组合数据之后的word二进制
*/
public static byte[] fillworddatabydomain(byte[] modelwordbyte, object obj) {
try {
class<?> aclass = obj.getclass();
field[] fields = aclass.getdeclaredfields();
map<string, object> data = new hashmap<>(fields.length);
for (field field : fields) {
propertydescriptor pd = new propertydescriptor(field.getname(), aclass);
method method = pd.getreadmethod();
string key = field.getname();
object value = method.invoke(obj);
if (value != null) {
data.put(key, value);
}
}
return fillworddatabymap(modelwordbyte, data);
} catch (exception e) {
e.printstacktrace();
return new byte[0];
}
}
/**
* 填充 word 模板(map数据格式)
*
* @param file word二进制
* @param data 要填充的数据
* @return 组合数据之后的word二进制
*/
public static byte[] fillworddatabymap(byte[] file, map<string, object> data) throws exception {
byte[] ret = null;
if (data == null || data.isempty()) {
return ret;
}
try (inputstream is = new bytearrayinputstream(file);
bytearrayoutputstream out = new bytearrayoutputstream()) {
document doc = new document(is);
documentbuilder builder = new documentbuilder(doc);
map<string, string> todata = new hashmap<>();
for (map.entry<string, object> entry : data.entryset()) {
string key = entry.getkey();
object value = entry.getvalue();
// 处理表格数据
if (value instanceof list && !key.equals("checkboxoptions")) {
datatable datatable = filllistdata((list) value, key, builder);
doc.getmailmerge().executewithregions(datatable);
}
// 图片插入
else if (value instanceof bufferedimage) {
builder.movetomergefield(key);
builder.insertimage((bufferedimage) value);
}
// 其他普通字段正常填充
else {
string valuestr = string.valueof(value);
if (value != null && !"null".equals(valuestr)) {
todata.put(key, valuestr);
}
}
}
// 执行普通字段合并
string[] fieldnames = new string[todata.size()];
string[] values = new string[todata.size()];
int i = 0;
for (map.entry<string, string> entry : todata.entryset()) {
fieldnames[i] = entry.getkey();
values[i] = entry.getvalue();
i++;
}
doc.getmailmerge().execute(fieldnames, values);
doc.save(out, saveoptions.createsaveoptions(saveformat.docx));
ret = out.tobytearray();
}
return ret;
}
/**
* 勾选段落中的复选框字段(适用于 aspose.words 18.8)
*/
private static void checkthecheckbox(paragraph paragraph) throws exception {
fieldcollection fields = paragraph.getrange().getfields();
int count = fields.getcount();
for (int i = 0; i < count; i++) {
com.aspose.words.field field = fields.get(i);
if (field.gettype() == fieldtype.field_form_check_box) {
// 设置字段结果为 "✓" 表示勾选(或根据模板实际显示字符调整)
setcheckboxchecked(field, true);
}
}
}
private static void setcheckboxchecked(com.aspose.words.field field, boolean checked) throws exception {
if (checked) {
field.setresult("✓"); // 根据模板中实际勾选状态设置
} else {
field.setresult("□"); // 可选:取消勾选
}
}
/**
* 封装 list 数据到 word 模板中(word表格)
*
* @param list 数据
* @param tablename 表格列表变量名称
* @return word表格数据datatable
*/
private static datatable filllistdata(list<object> list, string tablename, documentbuilder builder) throws exception {
//创建datatable,并绑定字段
datatable datatable = new datatable(tablename);
for (object obj : list) {
//创建datarow,封装该行数据
datarow datarow = datatable.newrow();
class<?> objclass = obj.getclass();
field[] fields = objclass.getdeclaredfields();
for (int i = 0; i < fields.length; i++) {
field field = fields[i];
datatable.getcolumns().add(fields[i].getname());
propertydescriptor pd = new propertydescriptor(field.getname(), objclass);
method method = pd.getreadmethod();
datarow.set(i, method.invoke(obj));
}
datatable.getrows().add(datarow);
}
return datatable;
}
// private static license license = null;
/**
* 加载 license
* 由于 aspose是收费的,若没有 license,则会出现水印。
*/
static {
try {
inputstream is = contractutil.class.getresourceasstream("/license.xml");
license license = new license();
license.setlicense(is);
} catch (exception e) {
throw new runtimeexception("自动加载aspose证书文件失败!");
}
}
}
三、测试
main方法测试,可以根据实际需求改为response输出或者上传到存储服务器后返回链接地址。
public static void main(string[] args) throws ioexception {
fillworddto fillworddto = fillworddto.create();
// 读取模板文件
byte[] modelbyte = files.readallbytes(paths.get("e:\\project\\spring-demo\\src\\main\\resources\\templates\\test.docx"));
// 调用工具类,获取填充数据后的文件
byte[] resultbyte = contractutil.fillworddatabydomain(modelbyte, fillworddto);
// 处理该二进制文件,此处处理为输出到桌面
file resultfile = new file("c:\\users\\lenovo\\desktop\\demo.docx");
fileoutputstream fos = new fileoutputstream(resultfile);
fos.write(resultbyte);
fos.close();
}
四、运行结果

五、注意事项
在编辑word域代码时,有时会有隐藏的代码导致填充失败
found end of mail merge region 'experiencelist' that does not match start of mail merge region 'jllist'.
出现以上错误或者想查看域代码,按如下操作:
在word中依次点击「文件→选项→高级」,在「显示文档内容」区域勾选「显示域代码而非域值」,找到报错域代码后删除,重新添加域就解决了。
以上就是使用java填充word模板的操作指南的详细内容,更多关于java填充word模板的资料请关注代码网其它相关文章!
发表评论