Java填充Word模板

文章目录

  • 前言
  • 一、设置word模板
    • 普通字段
    • 列表字段
    • 复选框
  • 二、代码
    • 1. 引入POM
    • 2. 模板放入项目
    • 3.代码
      • 实体类
      • 工具类
  • 三、测试
  • 四、运行结果
  • 五、注意事项


前言

最近有个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>

jar包和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) { // handmadeColorModel 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中依次点击「文件→选项→高级」,在「显示文档内容」区域勾选「显示域代码而非域值」,找到报错域代码后删除,重新添加域就解决了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/90726.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/90726.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MYSQL8】springboot项目,开启ssl证书安全连接

文章目录一、开启ssl证书1、msysql部署时默认开启ssl证书2、配置文件3、创建用户并指定ssl二、添加Java信任库1、使用 keytool 导入证书2、验证证书是否已导入三、修改连接配置一、开启ssl证书 1、msysql部署时默认开启ssl证书 可通过命令查看&#xff1a; SHOW VARIABLES L…

Telegraf vs. Logstash:实时数据处理架构中的关键组件对比

在现代数据基础设施中&#xff0c;Telegraf 和 Logstash 是两种广泛使用的开源数据收集与处理工具&#xff0c;但它们在设计目标、应用场景和架构角色上存在显著差异。本文将从实时数据处理架构、时序数据库集成、消息代理支持等方面对比两者的核心功能&#xff0c;并结合实际应…

Vue Vue-route (4)

Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue-route 编程式导航和几种路由 目录 编程式导航 详情组件 创建组件 设置路由 电影列表 传参 另一种方式 动态路由 命名路由 别名 总结 编程式导航 点击电影列表 跳转电影详情 详情组件 创建组件 在views中创…

存在两个cuda环境,在conda中切换到另一个

进入 openmmlab 环境 conda activate openmmlab 设置环境变量为 CUDA 12.4&#xff08;只影响当前 shell 会话&#xff09; export PATH/usr/local/cuda-12.4/bin:PATHexportLDLIBRARYPATH/usr/local/cuda−12.4/lib64:PATH export LD_LIBRARY_PATH/usr/local/cuda-12.4/lib64:…

Django 视图(View)

1. 视图简介 视图负责接收 web 请求并返回 web 响应。视图就是一个 python 函数,被定义在 views.py 中。响应可以是一张网页的 HTML 内容、一个重定向、一个 404 错误等等。响应处理过程如下图: 用户在浏览器中输入网址:www.demo.com/1/100Django 获取网址信息,去除域名和端…

HarmonyOS基础概念

一、OpenHarmony、HarmonyOS和Harmony NEXT区别OpenHarmony是由开放原子开源基金会&#xff08;OpenAtom Foundation&#xff09;孵化及运营的开源项目&#xff0c;开放原子开源基金会由华为、阿里、腾讯、百度、浪潮、招商银行、360等十家互联网企业共同发起组建。目标是面向全…

spark3 streaming 读kafka写es

1. 代码 package data_import import org.apache.spark.sql.{DataFrame, Row, SparkSession, SaveMode} import org.apache.spark.sql.types.{ArrayType, DoubleType, LongType, StringType, StructField, StructType, TimestampType} import org.apache.spark.sql.functions._…

【跟着PMP学习项目管理】每日一练 - 3

1、你是一个建筑项目的项目经理。电工已经开始铺设路线,此时客户带着一个变更请求来找你。他需要增加插座,你认为这会增加相关工作的成本。你要做的第一件事? A、拒绝做出变更,因为这会增加项目的成本并超出预算 B、参考项目管理计划,查看是否应当处理这个变更 C、查阅…

CentOS 安装 JDK+ NGINX+ Tomcat + Redis + MySQL搭建项目环境

目录第一步&#xff1a;安装JDK 1.8方法 1&#xff1a;安装 Oracle JDK 1.8方法 2&#xff1a;安装 OpenJDK 1.8第二步&#xff1a;使用yum安装NGINX第三步&#xff1a;安装Tomcat第四步&#xff1a;安装Redis第五步&#xff1a;安装MySQL第六步&#xff1a;MySQL版本兼容性问题…

如何设计一个登录管理系统:单点登录系统架构设计

关键词&#xff1a;如何设计一个登录管理系统、登录系统架构、用户认证、系统安全设计 &#x1f4cb; 目录 开篇&#xff1a;为什么登录系统这么重要&#xff1f;整体架构设计核心功能模块安全设计要点技术实现细节性能优化策略总结与展望 开篇&#xff1a;为什么登录系统这么…

论迹不论心

2025年7月11日&#xff0c;16~26℃&#xff0c;阴 紧急不紧急重要 备考ing 备课不重要 遇见&#xff1a;免费人格测试 | 16Personalities&#xff0c;下面是我的结果 INFJ分析与优化建议 User: Anonymous (隐藏) Created: 2025/7/11 23:38 Updated: 2025/7/11 23:43 Exported:…

【面板数据】省级泰尔指数及城乡收入差距测算(1990-2024年)

对中国各地区1990-2024年的泰尔指数、城乡收入差距进行测算。本文参考龙海明等&#xff08;2015&#xff09;&#xff0c;程名望、张家平&#xff08;2019&#xff09;的做法&#xff0c;采用泰尔指数测算城乡收入差距。参考陈斌开、林毅夫&#xff08;2013&#xff09;的做法&…

http get和http post的区别

HTTP GET 和 HTTP POST 是两种最常用的 HTTP 请求方法&#xff0c;它们在用途、数据传输方式、安全性等方面存在显著差异。以下是它们的主要区别&#xff1a;1. 用途GET&#xff1a;主要用于请求从服务器获取资源&#xff0c;比如获取网页内容、查询数据库等。GET 请求不应该用…

I2C集成电路总线

&#xff08;摘要&#xff1a;空闲时&#xff0c;时钟线数据线都是高电平&#xff0c;主机发送数据前&#xff0c;要在时钟为高电平时&#xff0c;把数据线从高电平拉低&#xff0c;数据发送采取高位先行&#xff0c;时钟线低电平时可以修改数据线&#xff0c;时钟线高电平时要…

为了安全应该使用非root用户启动nginx

nginx基线安全&#xff0c;修复步骤。主要是由于使用了root用户启动nginx。为了安全应该使用非root用户启动nginx一、检查项和问题检查项分类检查项名称身份鉴别检查是否配置Nginx账号锁定策略。服务配置检查Nginx进程启动账号。服务配置Nginx后端服务指定的Header隐藏状态服务…

论文解析篇 | YOLOv12:以注意力机制为核心的实时目标检测算法

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。长期以来&#xff0c;改进YOLO框架的网络架构一直至关重要&#xff0c;但尽管注意力机制在建模能力方面已被证明具有优越性&#xff0c;相关改进仍主要集中在基于卷积神经网络&#xff08;CNN&#xff09;的方法上。这是因…

学习C++、QT---20(C++的常用的4种信号与槽、自定义信号与槽的讲解)

每日一言相信自己&#xff0c;你比想象中更接近成功&#xff0c;继续勇往直前吧&#xff01;那么我们开始用这4种方法进行信号与槽的通信第一种信号与槽的绑定方式我们将按键右键后转到槽会自动跳转到这个widget.h文件里面并自动生成了定义&#xff0c;我们要记住我们这个按钮叫…

Anolis OS 23 架构支持家族新成员:Anolis OS 23.3 版本及 RISC-V 预览版发布

自 Anolis OS 23 版本发布之始&#xff0c;龙蜥社区就一直致力于探索同源异构的发行版能力&#xff0c;从 Anolis OS 23.1 版本支持龙芯架构同源异构开始&#xff0c;社区就在持续不断地寻找更多的异构可能性。 RISC-V 作为开放、模块化、可扩展的指令集架构&#xff0c;正成为…

4万亿英伟达,凭什么?

CUDA正是英伟达所有神话的起点。它不是一个产品&#xff0c;而是一个生态系统。当越多的开发者使用CUDA&#xff0c;就会催生越多的基于CUDA的应用程序和框架&#xff1b;这些杀手级应用又会吸引更多的用户和开发者投身于CUDA生态。这个正向飞轮一旦转动起来&#xff0c;其产生…

Unity3D iOS闪退问题解决方案

前言 在Unity3D开发中解决iOS闪退问题需要系统性排查&#xff0c;以下是关键步骤和解决方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;希望大家可以点击进来一起交流一下开发经验呀&#xff01; 1. 获取崩溃日志&#xff08;关键第一步&#xff…