java对word文档预设参数填值并生成

news2024/11/24 20:26:20

目录

(1)定义word文档模板

(2)模板二次处理

处理模板图片,不涉及图片可以跳过

 处理模板内容

(3)java对word模板填值

(4)Notepad++的XML Tools插件安装


工作上要搞一个合同签署功能,小程序上登录人进入功能会对合同进行电子签名,然后后端根据登录人信息和合同word文档模板生成一个合同word文档并保存,踩了不少坑。一开始是用了apache的poi,使用简单,读取word模板,然后遍历每一个段落和节点,判断节点是不是我定义的参数名,是就替换文本值,本以为会很简单就能搞定,结果每次遍历获取的节点经常不完整,${name}获取的时候可能会被分成三段,【${】【name】【}】,导致参数替换不上去,想着直接获取所有文本信息,然后直接string的replace方法替换,结果也不行,他不给直接对段落进行修改,直接麻了,而且pio包引入后,如果当前springboot版本太低,有些包就会出问题,又得特别处理,贼麻烦,后来就换成了freemarker,支持替换图片,不过步骤有些繁琐。


(1)定义word文档模板


既然是对word文档进行填值,那肯定得先定义一个word文档模板了,建一个docx后缀的word文档,并设置好格式,参数等,比如我建了一个word文件名为:wordTemplate.docx,内容是这样的,参数名是用${}包起来


(2)模板二次处理


格式全部调好后保存,并把文件后缀名改成zip,让它变成压缩包,记住你的word模板一定要是docx后缀的,doc后缀是老版的,搞不了。

打开压缩包是这个样子。

进入word文件夹里面是这样的,我们只需要关注【document.xml】【_rels】【media】,如果你不打算放图片,那只关注【document.xml】就行了。


处理模板图片,不涉及图片可以跳过


打开_rels文件夹是这样的。

【document.xml.rels】文件就是存放图片和文档之间的关系,我们把它解压出来并打开。

【打开xml时没有格式化过的,我用了Notepad++打开的,并装了XML Tools插件,然后把它格式化后才成了这个样子的】具体插件安装在文章最后面讲。


接着讲,image1名称是word文档生成的,为了后面填值方便区别,把 media/image1.png 改成 media/headImage.png,然后保存,回到压缩包里面,把原来的【document.xml.rels】替换掉,再打开【media】文件夹,会发现里面有个图片,名为image1.png,就是我们提前放入的头像,我们得把它改名成【headImage.png】,因为上一步,我们修改了【document.xml.rels】文件的映射,这里也得改。

这时候再把压缩包后缀改成docx,你会发现一样还能打开,不过这里我们改回去打开检查没问题后,再把后缀改回成zip。


这里再说一个点,如果模板图片太多,势必会造成模板文件很大,我们可以找一个透明或纯白色的宽高都是1px的图片, 反正很小的图片就行,名字改成【media】里面的图片名,放入【media】里面,相当于占位。也可以一开始创建word模板的时候,放图片直接放这个小体积的图片,把宽高调整到合适的就行。


 处理模板内容


打开压缩包把【document.xml】模板内容文件解压出来打开并xml格式化,找到你设置的参数名。

这时候会发现,参数名乱了,${name}可能被分隔七零八落,我们需要重新调一下,确保参数名完整,然后再保存。

这里再讲讲图片跟【document.xml】的关联,我们通过【document.xml.rels】图片映射文件可以看到,每一个图片标签都会有一个id,这个头像这个图片的id是rId4。

我们复制它去【document.xml】里面找,就会发现这个图片的标签以及格式了。

了解一下就行了,回归正题,我们调整好【document.xml】文件并保存,这个文件暂时不需要放回压缩包替换原来文件,到这一步,我们就有两个文件了,这两个文件缺一不可。


找一张图片替换模板里的头像。


(3)java对word模板填值


处理完word模板后,来到java代码。 

先引入包。不推荐用最新的包,我引了最新的包,用main方法测试的时候没问题,结果启动spring服务的时候,就不行了,报找不到那个版本的参数,后来降级就可以了。

<dependency>
	<groupId>org.freemarker</groupId>
	<artifactId>freemarker</artifactId>
	<version>2.3.28</version>
</dependency>

代码大致思路是这样的:

  1. 读取模板【document.xml】
  2. 值填充
  3. 重新生成一个【document.xml】
  4. 把这个新的【document.xml】和头像图片写入定义好的压缩包模板,也就是【wordTemplate.zip】
  5. 然后把压缩包输出成word文档

写一个main方法测试。


public static void main (String[] args) {
	// 模板存放路径
	String templastPath = "D:/A";
	// zip模板名称
	String zipTemplastName = "wordTemplate.zip";
	// zip模板存放路径
	String zipTemplastPath = new StringBuffer(templastPath).append(File.separator).append(zipTemplastName).toString();
	// docx文档模板名称
	String docxTemplateName = "document.xml";
	// 输出路径
	String outPath = "D:/A/out";
	// docx模板填充后输出路径,这里的【document.xml】不能改
	String outputDocxTemplatePath = new StringBuffer(outPath).append(File.separator).append("document.xml").toString();
	// 最终生成的docx文档输出路径,这里的word文档输出文件名随意
	String outputDocxFilePath = new StringBuffer(outPath).append(File.separator).append("output.docx").toString();
	File outPathFile = new File(outPath);
	if (!outPathFile.exists() && !outPathFile.mkdirs()) {
		throw new RuntimeException("输出路径创建失败");
	}
	try (
		FileOutputStream out = new FileOutputStream(outputDocxTemplatePath);
		OutputStreamWriter outputStreamWriter = new OutputStreamWriter(out);
		BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
		// zip模板压缩包输入流
		FileInputStream zipTemplastInput = new FileInputStream(zipTemplastPath);
		// 最终docx文档输出流
		FileOutputStream finalDocxOutput = new FileOutputStream(outputDocxFilePath);
	){
		// 将要替换的值
		Map<String, Object> map = new HashMap<String, Object>() {{
			put("name", "韩西景");
			put("sex", "男");
			put("age", "51");
			put("homeAddress", "广东省广州市白云区景山路3612号");
		}};

		//创建配置实例 VERSION_2_3_28是pom文件引入时的版本号
		Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
		//设置编码
		configuration.setDefaultEncoding("UTF-8");
		// 设置模板路径
		configuration.setDirectoryForTemplateLoading(new File(templastPath));
		// 获取xml模板
		Template template = configuration.getTemplate(docxTemplateName);
		// 参数值填充
		template.process(map, bufferedWriter);
		// 读取模板zip压缩包
		ZipInputStream zipInputStream = ZipUtils.wrapZipInputStream(zipTemplastInput);
		// 最终生成的word文档输出流
		ZipOutputStream zipOutputStream = ZipUtils.wrapZipOutputStream(finalDocxOutput);
		// zip压缩包要替换的项
		Map<String, String> replaceItemMap = new HashMap<String, String>(){{
			// 替换图片,本地路径可以,网络路径不行
			// put("word/media/headImage.png", "D:/A/headImage.png");
			// 替换图片,base64写入
			put("word/media/headImage.png", new StringBuffer("data:image/png;base64,").append(imageToBase64("D:/A/headImage.png")).toString());
			// 替换内容
			put("word/document.xml", outputDocxTemplatePath);
		}};
		ZipUtils.replaceItem(zipInputStream, zipOutputStream, replaceItemMap);
	} catch (Exception e) {
		System.out.println("报错了,这里自己打log啥的,该处理的处理");
	} finally {
		// 删除填充后xml模板
		new File(outputDocxTemplatePath).delete();
	}
}

/**
 * 图片转base64
 * @param inputPath 图片路径
 * @return base64字符串
 */
private static String imageToBase64(String inputPath) {
	File file = new File(inputPath);
	ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
	try(
		FileInputStream fileInputStream = new FileInputStream(inputPath);
		BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
	) {
		int b;
		byte[] bytes = new byte[4096];
		while ((b = bufferedInputStream.read(bytes)) > -1) {
			byteBuffer.put(bytes, 0, b);
		}
		return Base64.encodeBase64String(byteBuffer.array());
	} catch (Exception e) {
		System.out.println("报错了,这里自己打log啥的,该处理的处理");
	}
	return null;
}

执行之后,可以看到生成word文档了。


部署到服务器的话,就把模板文件放到服务器上面,然后配置模板路径,具体使用引入模板路径就行了。

至于word文档里面的表格,暂时没有研究。 


(4)Notepad++的XML Tools插件安装

仅用于格式化模板xml文件,方便调整xml文件,可装可不装,问题不大。 

插件安装:顶部菜单栏【插件】->【插件管理】打开后


好了,到这结束了,真够累人,这该死的996,永无止境的打工。


码字不易,于你有利,勿忘点赞 

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

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

相关文章

75101A 1553B总线测试模块

75101A 1553B总线测试模块 75101A 1553B总线测试模块是单通道多功能&#xff0c;符合CPCI/PXI总线的标准3U尺寸模块&#xff0c;可同时用作BC、RTs和BM&#xff0c;其中BM具有比特误码、highbit、lowbit、highword、lowword、校验错误、消息错误检测以及最大256M字节的数据捕…

VBA技术资料MF168:移动工作表为单独工作簿

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

提高开发效率之——工具介绍

一 . SerialDebug 串口调试工具 SerialDebug 是一个串口调试工具&#xff0c;它主要用于帮助电子工程师和软件开发者进行串口通信的调试工作。以下是 SerialDebug 工具的一些主要作用和特点: 基础串口通信功能&#xff1a;提供打开、关闭、接收、发送数据的基础串口操作。 数…

python项目运营时,出现,redis用户密码未设置问题,排查解决

一、问题描述&#xff1a; 在本地化开发过程中&#xff0c;pythonDjango运行项目&#xff0c;redis为本地windows版本&#xff0c;在设置过密码后&#xff0c;仍然会出现pythonDjango运行项目&#xff0c;终端日志显示如下&#xff1a; INFO info信息 ERROR redis数据库异常[&…

内网部署Prometheus-server结合grafana

目录 1.提取依赖包 2.所有节点安装chrony服务 2.1集群角色规划 2.2 所有节点安装chrony服务 2.3添加hosts文件解析(服务端) 2.4修改时间服务器 ​编辑 2.5重启chrony服务&#xff0c;使得配置生效 2.6修改客户端配置&#xff08;注意是客户端其他节点&#xff09; 2.…

韩语日常口语留学韩语旅游韩语口语柯桥语言培训

韩语每日一词打卡&#xff1a;행정적[행정적]【名词】行政 原文:나라는 항상 행정적 명령을 통해 경제운행을 조절합니다. 意思&#xff1a;国家经常通过行政命令去调节经济的运行。 【原文分解】 1、항상[항상]总是 &#xff0c;经常 2、나라[나라]国家 3、명령[명:녕]命令 …

使用AI机器学习,轻松解决化合物配比优化问题

为什么需要化合物配比的优化&#xff1f; 在化合物制造行业中&#xff0c;化合物的配比是产品质量控制的关键环节。 化合物制造流程 目前&#xff0c;这一过程高度依赖于材料专家和工程技术人员的经验&#xff0c;通过反复试验来验证产品性能&#xff0c;确保其满足市场和客户的…

Spring Boot 学习第八天:AOP代理机制对性能的影响

1 概述 在讨论动态代理机制时&#xff0c;一个不可避免的话题是性能。无论采用JDK动态代理还是CGLIB动态代理&#xff0c;本质上都是在原有目标对象上进行了封装和转换&#xff0c;这个过程需要消耗资源和性能。而JDK和CGLIB动态代理的内部实现过程本身也存在很大差异。下面将讨…

队列与循环队列

目录 1. 前言&#xff1a; 2. 队列 2.1 队列的概念 2.2 队列的实现 2.3 队列的声明 2.4 队列的初始化 2.5 队列的入队 2.6 队列的出队 2.7 队列获取队头元素 2.8 队列获取队尾元素 2.9 队列获取有效数据个数 2.10 队列判断是否为空 2.11 打印队列 2.12 销毁队列 …

机器发货到菲律宾的完整流程 广东智慧物流

机器发货到菲律宾的完整流程 广东智慧物流 &#x1f31f;【机器发货到菲律宾完全攻略】&#x1f31f; 机器发货到菲律宾的完整流程 广东智慧物流 为你整理了一份超详细的机器发货到菲律宾的攻略&#xff01;海运14天轻松到达&#xff0c;让你无后顾之忧&#xff01;&#x1f…

【编译原理】总览

1 字母表 字母表&#xff1a; 用∑表示&#xff0c;它是一个有穷符号集合 符号&#xff1a;字母、数字、标点符号... 例如&#xff1a;二进制字母表为{0&#xff0c;1}&#xff0c;ASCII字符集 2 字母表的运算 字母表上的乘积&#xff1a; ∑1∑2{ab | a属于∑1&#xff0c;b…

MySQL—存储过程(详细介绍与基本语法)

目录 一、存储过程——介绍 &#xff08;1&#xff09;基本介绍 &#xff08;2&#xff09;基本特点 二、存储过程——语法 &#xff08;1&#xff09;基本语法 创建 调用 &#xff08;2&#xff09;实操&#xff08;创建和调用&#xff09; 1、创建一个叫 "p1&qu…

SAP CO11N BAPI_PRODORDCONF_CREATE_TT连续报工异步更新导致COGI解决方案

背景&#xff1a; 之前上一个项目上有同事碰到一个问题&#xff0c;外围接口数据进入SAP&#xff0c;可能会对同一工单同一工序进行连续多次报工&#xff0c;并且工序控制码配置的是会自动货物移动的&#xff0c;所以正常来说&#xff0c;调用完BAPI完之后除了报工数量会更新之…

indexedDB---浏览器本地数据库实现增删改查

关于indexedDB indexedDB的基本使用&#xff0c;可以参考&#xff1a;indexedDB---掌握浏览器内建数据库的基本用法_indexdb浏览器使用-CSDN博客 indexedDB是浏览器本地数据库&#xff0c;既然是数据库就能够实现增删改查&#xff0c;了解了indexedDB的基本使用后&#xff0c…

怎么把不同的文件做成二维码?多种文件类型在线生码的制作方法

扫码来展示文件是现在很多场景下会使用的一种内容展示方法&#xff0c;这种方式能够简化其他人获取文件的流程&#xff0c;从而提升文件传播的效率&#xff0c;用户可以存储二维码&#xff0c;随时扫码查看文件内容。文件生成二维码支持多种类型的文件使用&#xff0c;比如ppt、…

Element 页面滚动表头置顶

在开发后台管理系统时&#xff0c;表格是最常用的一个组件&#xff0c;为了看数据方便&#xff0c;时常需要固定表头。 如果页面基本只有一个表格区域&#xff0c;我们可以根据屏幕的高度动态的计算出一个值&#xff0c;给表格设定一个固定高度&#xff0c;这样表头就可以固定…

Transformer实战——摘要生成

本文借助Huggingface Tranformer库完成一个摘要生成任务&#xff0c;参考自https://github.com/datawhalechina/learn-nlp-with-transformers/&#xff0c;在本文&#xff0c;将展示如何使用简单的加载数据集&#xff0c;同时针对相应的任务使用transformer中的Trainer接口对模…

司美格鲁肽在中国获批!深度解析报告附上

在中国&#xff0c;肥胖问题日益严重&#xff0c;但有效的治疗方法却相对匮乏。然而&#xff0c;这一现状随着国家药品监督管理局&#xff08;NMPA&#xff09;对诺和诺德公司研发的司美格鲁肽注射液&#xff08;商品名&#xff1a;诺和盈&#xff09;的批准而得到改变。6月25日…

hadoop离线与实时的电影推荐系统-计算机毕业设计源码10338

摘 要 随着互联网与移动互联网迅速普及&#xff0c;网络上的电影娱乐信息数量相当庞大&#xff0c;人们对获取感兴趣的电影娱乐信息的需求越来越大,个性化的离线与实时的电影推荐系统 成为一个热门。然而电影信息的表示相当复杂&#xff0c;己有的相似度计算方法与推荐算法都各…

文件管理器加载缓慢-禁用文件类型自动发现功能

文件管理器加载缓慢-禁用文件类型自动发现功能 右键“Shell”项&#xff0c;选择新建“字符串值” “FolderType”&#xff0c;数值为 NotSpecified。