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

目录

(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.mzph.cn/web/34823.shtml

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

相关文章

C#与工业自动化结合还有搞头吗?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「c#的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;当然有搞头&#xff01;C#是一…

Java 8 新特性:Lambda表达式让你的代码焕然一新——掌握它,让编程变得轻松又高效!

前言 Java 8 是 Java 发展史上的一次重要里程碑。作为企业级开发语言&#xff0c;它在性能和功能上做了巨大的提升。这其中&#xff0c;Lambda表达式是一个关键的新特性&#xff0c;它为 Java 语言带来了函数式编程的概念。本篇文章将深入探讨Lambda表达式&#xff0c;并结合热…

element 问题整合

没关系&#xff0c;凡事发生必有利于我 文章目录 一、el-table 同级数据对齐及展开图标的位置问题二、el-table 勾选框为圆角及只能勾选一个 一、el-table 同级数据对齐及展开图标的位置问题 element 官方提供的扩展tree型数据在表格里默认是靠左边对齐&#xff0c;项目需求需要…

超越边界:探索深度学习的泛化力量

深度学习的泛化能力 一. 简介1.1 深度学习的定义1.2 什么是泛化能力1.3 深度学习模型的泛化能力1.4 提升深度学习模型的泛化能力 二. 泛化能力的重要性2.1 深度学习中泛化能力的作用2.1.1 防止过拟合2.1.2 处理噪声和不完整数据2.1.3 对于数据分布的变化具有适应性 2.2 泛化能力…

Gitlab合并代码并解决冲突演示

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

【SQL】数据操作语言(DML) - 删除数据:精细管理数据的利刃

目录 前言 DELETE语句的基础使用 删除指定记录 清空表与删除表数据的区别 注意 前言 在数据库管理的日常工作中&#xff0c;数据的删除是一项需要格外小心的操作&#xff0c;因为一旦数据被删除&#xff0c;往往难以恢复。数据操作语言(DML)中的DELETE语句&am…

景区智能厕所系统让旅游更便捷、更舒适

在旅游业蓬勃发展的今天&#xff0c;游客对于旅游体验的要求越来越高&#xff0c;而景区的基础设施建设也在不断创新和完善。其中&#xff0c;景区智能厕所系统的出现&#xff0c;无疑为游客带来了更加便捷、舒适的旅游体验。 一、智能厕所系统首先体现在其精准的定位和引导功能…

three.js 第六节 - 纹理以及贴图【.hdr文件(hdr贴图)】- 色彩空间

素材 这是素材 更多素材、案例、项目 好几个G一共&#xff0c;加我q178373168&#xff0c;60大洋拿走 源码 源码 // ts-nocheck // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls…

【LeetCode 274】H指数

1. 题目 2. 分析 这题没啥难度&#xff0c;需要熟练运用Python API。 sort(reverseTrue)可以用于排序List&#xff0c;并且倒序排序。 3. 代码 class Solution:def hIndex(self, citations: List[int]) -> int:citations.sort(reverseTrue)res 0for idx,cite in enume…

【毛毛虫案例-拖拽 Objective-C语言】

一、这个毛毛虫案例啊,是这个样子的, 1.首先,你这个脑袋,这个蓝色的脑袋,它是可以拽起来的, 下面的红色球,一个一个中心点之间,相互去附着, 其他的红色球,是拖不起来的, 只有这个蓝色的东西,可以拽起来,这个蓝色的View,还有重力, 这个蓝色的View,我在拖动它…

[深度学习] 变分自编码器VAE

变分自编码器&#xff08;Variational Autoencoders, VAEs&#xff09;是一种生成模型 Tutorial on Variational Autoencoders&#xff0c;它结合了概率图模型和深度学习&#xff0c;通过学习数据的潜在表示来生成新的数据样本。VAEs在数据生成、异常检测、数据压缩等领域具有广…

如何应对UI测试自动化的不稳定循环!

以下为作者观点&#xff1a; 当我加入UI自动化团队时&#xff0c;我很高兴能为新功能的自动化测试用例开发做出贡献。然而&#xff0c;我很快意识到团队花费了大量时间来修复之前迭代中不稳定的测试。这种情况让我感到困惑&#xff0c;因为当自动化测试脚本已知不稳定时&#…

【Windows 常用工具系列 17 -- windows bat 脚本多参数处理】

请阅读【嵌入式开发学习必备专栏】 文章目录 bat 脚本命令行参数使用示例多参数处理使用示例遍历所有参数 bat 脚本命令行参数 在Windows批处理&#xff08;.bat&#xff09;脚本中接收命令行参数是一个常见的需求&#xff0c;这样的脚本能够根据提供的参数执行不同的操作。命…

Kubernetes之Controller详解

本文尝试从Kubernetes Controller的种类、交互逻辑、最佳实践、伪代码示例及历史演进5个方面对其进行详细阐述&#xff0c;希望对您有所帮助&#xff01; 一、Kubernetes Controller种类 Kubernetes Controller Manager 是 Kubernetes 集群的核心组件之一&#xff0c;负责管理…

无线幅频仪制作(WiFi通信)-含STM32源程序,JAVA上位机与设计报告

资料下载地址&#xff1a;无线幅频仪制作(WiFi通信)-含STM32源程序,JAVA上位机与设计报告 目录 项目功能 1、 系统方案1.1 比较与选择 1.1.1 控制器的论证与选择 1.1.2 信号源的论证与选择 1.1.3 放大器模块的论证与选择 1.1.4 键盘与显示模块的论证与选择 1.1.5 网络通…

[保姆级教程]uniapp小程序获取右上角胶囊位置信息

文章目录 导文使用uni.getMenuButtonBoundingClientRect();方法实现完整案例 隐藏默认导航栏&#xff1a;全局隐藏当前页面隐藏 导文 uniapp小程序获取右上角胶囊位置信息 使用uni.getMenuButtonBoundingClientRect();方法实现 <script>const menuButtonInfo uni.getMe…

logstash配置文件中明文密码加密

1 案例背景 应用配置文件中禁止使用明文密码&#xff0c;需要加密处理 上图中&#xff0c;红框打码位置为es的明文密码&#xff0c;需要对其进行处理 2 创健keystore文件 /rpa/logstash/bin/logstash-keystore --path.settings /rpa/isa/conf/logstash/ create 注&#xff1…

记录正则提取文章

收到了个word版的电子书&#xff0c;需要拆分并转换为md存储到数据库中&#xff0c;便于搜索&#xff0c;记录下用正则提取文章的过程 word原文中有目录&#xff0c;可提取出目录后&#xff0c;在正文中根据目录来正则提取文章 正则的多行匹配 在匹配大量文章的时候&#xff…

llm-universe | 四. 构建RAG应用

构建RAG应用 一.将LLM 接入 LangChain二.构建检索问答链1.加载向量数据库2.创建一个 LLM3.构建检索问答链4.检索问答链效果测试5.添加历史对话的记忆功能5.1 记忆&#xff08;Memory&#xff09;5.2 对话检索链&#xff08;ConversationalRetrievalChain&#xff09; 三. 部署知…

ts可选参数

可选参数 参数后加个问号&#xff0c;代表这个参数是可选的 function bdd(x:number,y?:number){return x y } console.log(bdd(2,3)) function bdd(x:number,y?:number){return x y } console.log(bdd(2))