java从word模板生成.doc和.wps文件

news/2025/9/30 19:12:29/文章来源:https://www.cnblogs.com/yclh/p/19121676

当遇到要生成一个word文档(证明文件等)的需求时,就可以考虑使用word模板生成.doc和.wps文件

一、需求

1、生成如下这样的订单数据.doc文件,红框部分是变化的,其余部分是固定的

图片

2、生成如下这样的书籍列表,书的个数不固定是动态的。

图片

二、使用Docx4j实现 

1、引入依赖

<!-- Docx4j 核心依赖 -->
<dependency><groupId>org.docx4j</groupId><artifactId>docx4j-core</artifactId><version>8.3.0</version> <!-- 兼容 JDK 1.8 的稳定版本 -->
</dependency><!-- 处理变量替换所需依赖 -->
<dependency><groupId>org.docx4j</groupId><artifactId>docx4j-JAXB-ReferenceImpl</artifactId><version>8.3.0</version>
</dependency><!-- Velocity模板引擎 -->
<dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version>
</dependency><dependency><groupId>org.docx4j</groupId><artifactId>docx4j-JAXB-Internal</artifactId><version>8.3.0</version>
</dependency>

注: 前两个完成第一个需求就够了,后两个使用模板完成后一个需求

 2、制作模板

变量的地方用${}

模板contract_template.docx内容如下:

图片

 

模板book_template.docx内容如下:

使用foreach进行循环

图片

3、代码

Docx4jService
package com.example.demo.demos;import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.Document;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;import javax.xml.bind.JAXBElement;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Map;@Service
public class Docx4jService {/*** 根据模板生成Word文档(修复没有write方法的问题)*/public byte[] generateWord(String templatePath, Map<String, String> data) throws Exception {// 1. 加载模板ClassPathResource resource = new ClassPathResource(templatePath);WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(resource.getInputStream());MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();// 2. 获取文档XML内容Document document = mainDocumentPart.getJaxbElement();List<Object> content = document.getBody().getContent();// 3. 手动替换变量replaceVariables(content, data);// 4. 写回并生成字节数组(使用save方法替代write方法)try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {// 关键修复:用save方法替代write方法wordMLPackage.save(out);return out.toByteArray();}}/*** 递归替换文档中的变量(修复Text节点无法识别问题)*/@SuppressWarnings("unchecked")private void replaceVariables(List<Object> content, Map<String, String> data) {for (int i = 0; i < content.size(); i++) {Object obj = content.get(i);// 处理JAXB包装的元素(解开外层包装)if (obj instanceof JAXBElement) {obj = ((JAXBElement<?>) obj).getValue();}// 关键修复:处理运行元素(R),Text节点一定在R里面if (obj instanceof org.docx4j.wml.R) {org.docx4j.wml.R run = (org.docx4j.wml.R) obj;// 遍历R元素中的内容,寻找Text节点replaceVariables(run.getContent(), data);}// 现在可以找到Text节点了else if (obj instanceof org.docx4j.wml.Text) {org.docx4j.wml.Text text = (org.docx4j.wml.Text) obj;String value = text.getValue();// 遍历所有变量进行替换for (Map.Entry<String, String> entry : data.entrySet()) {String key = "${" + entry.getKey() + "}";if (value.contains(key)) {value = value.replace(key, entry.getValue());text.setValue(value);}}}// 处理段落(P):继续遍历段落中的内容(会包含R元素)else if (obj instanceof org.docx4j.wml.P) {org.docx4j.wml.P p = (org.docx4j.wml.P) obj;replaceVariables(p.getContent(), data);}// 处理表格(Tbl):继续遍历表格中的内容else if (obj instanceof org.docx4j.wml.Tbl) {org.docx4j.wml.Tbl tbl = (org.docx4j.wml.Tbl) obj;replaceVariables(tbl.getContent(), data);}}}}

 

Book
package com.example.demo.demos;public class Book {private String name;private String author;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public Book(String name, String author) {this.name = name;this.author = author;}
}

 WordController类

 

package com.example.demo.demos;import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.docx4j.Docx4J;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.wml.Document;@RestController
@RequestMapping("/word")
public class WordController {@Autowiredprivate Docx4jService docx4jService;/*** 生成并下载 Word 文档*/@GetMapping("/generate")public ResponseEntity<byte[]> generateWord() throws Exception {// 1. 准备模板变量数据Map<String, String> data = new HashMap<>();data.put("username", "张三");data.put("orderNo", "ORD20250911001");data.put("amount", "1000.00");data.put("date", "2025-09-11");// 2. 调用服务生成 Word 字节数组byte[] wordBytes = docx4jService.generateWord("templates/contract_template.docx", data);// 3. 配置响应头,实现文件下载String fileName = URLEncoder.encode("订单详情.docx", "UTF-8");HttpHeaders headers = new HttpHeaders();headers.add("Content-Disposition", "attachment; filename=" + fileName);headers.add("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");return new ResponseEntity<>(wordBytes, headers, HttpStatus.OK);}static {try {Properties props = new Properties();props.setProperty("resource.loader", "class");props.setProperty("class.resource.loader.class","org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");Velocity.init(props);} catch (Exception e) {throw new RuntimeException("Velocity引擎初始化失败", e);}}@GetMapping("/export")public void exportBooks(HttpServletResponse response) {try (InputStream templateStream = new ClassPathResource("templates/book_template.docx").getInputStream()) {WordprocessingMLPackage template = WordprocessingMLPackage.load(templateStream);MainDocumentPart mainPart = template.getMainDocumentPart();//生成books内容List<Book> books = new ArrayList<>();for (int i = 1; i <= 3; i++) {books.add(new Book("Java开发指南" + i, "作者" + i));}VelocityContext context = new VelocityContext();context.put("books", books);String xmlContent = mainPart.getXML();StringWriter writer = new StringWriter();Velocity.evaluate(context, writer, "bookTemplate", xmlContent);String processedXml = writer.toString();// 过滤非法标签String cleanedXml = cleanSdtElements(processedXml);// 替换主文档内容(使用docx4j的JAXB工具类)replaceMainDocumentContent(mainPart, cleanedXml);try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {Docx4J.save(template, baos);response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");String fileName = new String("1.docx".getBytes("UTF-8"), "ISO-8859-1");response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");response.setContentLength(baos.size());baos.writeTo(response.getOutputStream());response.flushBuffer();}} catch (Exception e) {e.printStackTrace();}}// 清理内容控件相关标签private String cleanSdtElements(String xml) {xml = xml.replaceAll("<w:sdt[^>]*>", "");xml = xml.replaceAll("<w:sdtPr[^>]*>", "");xml = xml.replaceAll("<w:dataBinding[^>]*>", "");xml = xml.replaceAll("<w:sdtContent[^>]*>", "");xml = xml.replaceAll("</w:sdt>", "");xml = xml.replaceAll("</w:sdtPr>", "");xml = xml.replaceAll("</w:dataBinding>", "");xml = xml.replaceAll("</w:sdtContent>", "");return xml;}// 关键修复:使用docx4j提供的Context获取JAXBContextprivate void replaceMainDocumentContent(MainDocumentPart mainPart, String xmlContent)throws JAXBException, XMLStreamException, UnsupportedEncodingException {// 使用docx4j的Context工具类获取预配置的JAXBContextUnmarshaller u = Context.jc.createUnmarshaller();XMLInputFactory xif = XMLInputFactory.newFactory();xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);try (ByteArrayInputStream bais = new ByteArrayInputStream(xmlContent.getBytes("UTF-8"))) {XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource(bais));Document doc = (Document) u.unmarshal(xsr);mainPart.setJaxbElement(doc);} catch (IOException e) {throw new RuntimeException("XML内容转换失败", e);}}}

 

 4、整体代码结构

图片

 5、使用postman 调用controller的接口就能下载到从模板生成的word文档

 

6、下载地址

 http://resources.kittytiger.cn/ 搜索java从word模板生成.doc和.wps文件

 

 


















 

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

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

相关文章

做我女朋友好不好手机网站汕头网站制作电话

一、介绍带有金属球的球形倾斜开关&#xff0c;它用于检测小角度的倾斜。图7.1 倾斜开关模块二、材料准备Arduino Uno 主板*1USB数据线*1倾斜开关模块*1杜邦线若干三、实验原理在倾斜开关中小球以不同的倾斜角度移动以造成触发电路的原理。倾斜开关模块使用双向传导的球形倾斜开…

分布式限流方案 - 详解

分布式限流方案 - 详解2025-09-30 19:07 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font…

炼石#8 T1

赛时很快想出切掉,感觉比t2恶心🤢,可能是二分做法的问题(bushi 想法简单,贪心想法保留m个最小的 ,于是二分k用来区分大数和小数 , 如果小于k/2为小数 , 显然小数是必拿 ,然后在每个小数之间最多只能有一个大…

中国建设银行卖狗年纪念币官方网站百度推广seo软件

标题&#xff1a;深入解析JVM内部机制&#xff1a;探索Java虚拟机的工作原理 摘要&#xff1a;本文将深入解析Java虚拟机&#xff08;JVM&#xff09;的内部机制&#xff0c;探索其工作原理。我们将从JVM的架构、内存管理、垃圾回收、即时编译器等方面进行讨论&#xff0c;并通…

详细介绍:《C++ Primer Plus》读书笔记 第二章 开始学习C++

详细介绍:《C++ Primer Plus》读书笔记 第二章 开始学习C++pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Conso…

网站后台数据库下载企业网站服务类型

纯HTML外贸公司通用企业html网站模板源码 源码地址&#xff1a;https://download.csdn.net/download/Highning0007/89150720

【虚拟机】“:域名解析出现暂时性错误”VMware配置DNS

前言 最近家里路由器换了,虚拟机忘记重新配置,导致下午在apt的时候出现这个错误环境 软件:VMware Workstation 17 Pro 系统:Windows 10 网络:使用NAT模式作为虚拟机网络 步骤2. 选中虚拟机使用的虚拟网络,点击NA…

十堰网站建设电话wordpress 登录地址

1.Hazelcast介绍 Hazelcast是Hazelcast公司开源的一款分布式内存数据库产品&#xff0c;提供弹性可扩展、高性能的分布式内存计算。并通过提供诸如Map&#xff0c;Queue&#xff0c;ExecutorService&#xff0c;Lock和JCache等Java的许多开发人员友好的分布式实现。 Hazelcast优…

双抗 ADC:如何突破传统 ADC 瓶颈,成为癌症治疗的精准杀伤利器?

在癌症靶向治疗领域,单克隆抗体(单抗)药物曾凭借 “精准识别肿瘤靶点” 的优势改变治疗格局,但随着临床应用深入,其局限性逐渐凸显 —— 癌症发病机制复杂,多靶点协同驱动肿瘤进展、单一靶点靶向易引发耐药、肿瘤…

通州网站建设公司如何建立自己的公司

前言&#xff1a;博主第一次接触MongoDB&#xff0c;看了一圈网上现有的教程&#xff0c;不是缺少细节就是有问题没交代清楚&#xff0c;特整理了一下自己安装运行的过程&#xff0c;从下载安装到开机自启&#xff0c;全程细节齐全、图文并茂、简单易懂。 目录 1. 从官网下载2…

电子商务网站建设预算表seo是指什么岗位

使用 netstat 检查端口 netstat 是一个命令行工具&#xff0c;可以提供有关网络连接的信息。 netstat - atulnp会显示所有端口和所有对应的程序&#xff0c;用grep管道可以过滤出想要的字段 -a &#xff1a;all&#xff0c;表示列出所有的连接&#xff0c;服务监听&#xff…

网站备案域名需要解析到备案服务器吗设计软件的软件

第一、搭建python环境 安装python 第二、下载Gstreamer 下载Gstreamer 第三、编写 GStreamer 插件 编写 GStreamer 官方资料 其他 第四、Gstreamer中文文档 中文文档

【软件架构设计(40)】数据库规范化与性能优化 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

AI+手搓第一个AI Agent“AI胜铭兰”

​ 上次开发了一个MCP云部署平台。时隔将近4个月,第二个项目AI Agent“AI胜铭兰”终于开发完且上线了。 项目介绍: 第二个项目是建立在第一个项目的基础上的。所以功能可以做到定制化开发。每个MCP开发好只需要配置下…

开发一套网站价格淘宝网站如何推广

因为有个需求&#xff0c;需要处理文件夹内所有txt文件&#xff0c;将txt里面的数据筛选&#xff0c;重新存储。 虽然手工可以做&#xff0c;但想到了python一直主张的是自动化测试&#xff0c;就想试着写一个自动化处理数据的程序。 一.分析数据格式 需要处理的数据是txt格式存…

基于JDK17的GC调优策略

基于JDK17的GC调优策略 一、JVM 参数分类(三类核心参数) JVM 参数按稳定性分为三类,不同类别对应不同使用场景和查看方式,具体如下表:参数类别 标识符号 稳定性 查看命令 常用示例标准参数 - 开头 所有 HotSpot 均…

成都网站建设案例单招网网站推广人员怎么算业绩

Spring Spring Cache 注释驱动的 Spring cache 缓存介绍使用 Spring 2.5 注释驱动的 IoC 功能虚拟化 libvirt kvm 虚拟机上网 – Bridge桥接KVM详解&#xff0c;太详细太深入了&#xff0c;经典Tomcat Linux机器同时运行两个tomcat点评&#xff1a;主要是那张图片的配置 Nginx …

福安 网站设计wordpress卸载插件

因个人需要&#xff0c;mkfs.ext3 但是项目中还没有这个命令 所以琢磨了半天 这里将其小记一下 在buildrootfsz中&#xff0c;需要将e2fsprogs 勾选上然后重新编译就好了 make menuconfig Target packages-> Filesystem and flash utilities-> e2fsprogs

【MC】我的世界schematic方块坐标提取转为json

前言 主包最近在搞mc广州塔灯光效果的复刻计划(请看合集),由于主包没学过着色器编程,但是目前而言,最大的问题已经转化为只要控制每个方块的灯光颜色就够了。 所以想着用three.js做个轻量化的广州塔灯光模拟器,然…

Jenkins+IIS+Bonobo.Git.Server 搭建适用dotnet开发者的小团队的devops环境

前言: 1、在一个小团队里面,可能我们既是开发(前端+后端)也是运维还是售前售后服务。我们样样都得会,但说实话,样样都不是很精!但是我们也梦,梦想着一切能像大厂那样,开发是开发,运维是运维,各司其职。作为…