2024 java easyexcel poi word模板填充数据,多个word合成一个word

先看效果

一、准备工作

1.word模版


2.文件路径

二、pom依赖

   <!--   easyexcel     --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.7</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version></dependency><!--   word export     --><dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.12.0</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.poi/poi --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.2</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.1.2</version></dependency>


三、两个工具类+自己的实体类(这里是用户作为示例)

1.自己的实体类

import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;import java.io.Serializable;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 用户信息 实体类** @author zhaoyan* @since 2024-04-19*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(value = "user")
public class User implements Serializable {/*** 用户id*/@Id(keyType = KeyType.Auto)private Integer id;/*** 姓名*/private String name;/*** 身份证号*/private String cardId;/*** 性别*/private String sex;}


2.合并word工具类


import java.io.*;
import java.util.*;import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;/*** 合并word工具类*/
public class MergeWordUtil {/*** 将多个Word文档文件合并成一个新的Word文档文件,* 并将合并后的内容保存到指定的目标路径中。** @param files      多个word文件* @param targetPath 目标存放地址* @throws Exception*/public static void mergeMroeWord(List<File> files, String targetPath) throws Exception {// 1. 打开目标路径对应的文件输出流 `dest`,并使用 try-with-resources 语句确保流在使用完毕后会被正确关闭。try (OutputStream dest = new FileOutputStream(targetPath);) {// 2. 创建一个 `ArrayList` 类型的 `documentList`,用于存储读取的多个Word文档对象。ArrayList<XWPFDocument> documentList = new ArrayList<>();XWPFDocument doc = null;// 3. 遍历传入的文件列表 `files`,对每个文件执行以下操作:for (File file : files) {try (FileInputStream in = new FileInputStream(file.getAbsoluteFile())) {// - 使用 `FileInputStream` 读取文件内容,并通过 `OPCPackage` 打开文件流,创建一个新的XWPFDocument对象 `document`。OPCPackage open = OPCPackage.open(in);XWPFDocument document = new XWPFDocument(open);// - 将读取的文档对象 `document` 添加到 `documentList` 中。documentList.add(document);} catch (FileNotFoundException e) {// - 如果读取文件时发生 `FileNotFoundException` 异常,则捕获并打印异常信息。e.printStackTrace();}}// 4. 遍历 `documentList` 中的文档对象:for (int i = 0; i < documentList.size(); i++) {doc = documentList.get(0);// - 如果是第一个文档(`i == 0`),在文档中创建一个新段落,并在新段落中添加一个换页符。if (i == 0) {documentList.get(i).createParagraph().createRun().addBreak(BreakType.PAGE);// appendBody(doc,documentList.get(i));// - 如果是最后一个文档(`i == documentList.size() - 1`),调用 `appendBody` 方法将当前文档内容追加到 `doc` 文档中。} else if (i == documentList.size() - 1) {appendBody(doc, documentList.get(i));// - 如果既不是第一个文档也不是最后一个文档,分别在文档中创建一个新段落并添加换页符,然后调用 `appendBody` 方法将当前文档内容追加到 `doc` 文档中。} else {documentList.get(i).createParagraph().createRun().addBreak(BreakType.PAGE);appendBody(doc, documentList.get(i));}}// 5. 最终将合并后的文档内容写入到目标路径对应的文件中,并确保 `doc` 不为null。assert doc != null;// 6. 如果在写入过程中发生异常,则会抛出异常。doc.write(dest);}}/*** 将一个文档对象的内容追加到另一个文档对象中,* 同时处理了文档中的图片数据,* 确保图片在合并后的文档中能够正确显示** @param src* @param append* @throws Exception*/public static void appendBody(XWPFDocument src, XWPFDocument append) throws Exception {// 1. 通过 `src.getDocument().getBody()` 和 `append.getDocument().getBody()` 分别获取源文档和追加文档的主体内容(CTBody对象)。CTBody src1Body = src.getDocument().getBody();CTBody src2Body = append.getDocument().getBody();// 2. 调用 `append.getAllPictures()` 方法获取追加文档中的所有图片数据,并将其存储在 `allPictures` 列表中。// 记录图片合并前及合并后的IDList<XWPFPictureData> allPictures = append.getAllPictures();// 3. 创建一个 `HashMap` 对象 `map`,用于记录图片在合并前和合并后的关系。Map<String, String> map = new HashMap<String, String>();// 4. 遍历追加文档中的所有图片数据 `allPictures`,对每个图片执行以下操作:for (XWPFPictureData picture : allPictures) {// - 获取图片在追加文档中的关系ID,并将其存储在 `before` 变量中。String before = append.getRelationId(picture);// - 将图片数据添加到源文档中,并指定图片类型为PNG,获取添加后的图片关系ID,并将其存储在 `after` 变量中。String after = src.addPictureData(picture.getData(), Document.PICTURE_TYPE_PNG);// - 将 `before` 和 `after` 的对应关系存储在 `map` 中。map.put(before, after);}// 5. 调用另一个方法 `appendBody`,传入源文档的主体内容、追加文档的主体内容和图片关系ID的映射,将追加文档的内容合并到源文档中。appendBody(src1Body, src2Body, map);}/*** 将两个文档对象的主体内容进行合并,* 并在合并过程中替换图片ID,* 最终更新源文档对象的主体内容** @param src    源文档的主体内容,类型为 `CTBody`。* @param append 追加文档的主体内容,类型为 `CTBody`。* @param map    存储图片ID替换关系的映射,类型为 `Map<String, String>`。* @throws Exception*/private static void appendBody(CTBody src, CTBody append, Map<String, String> map) throws Exception {// 1. 创建一个 `XmlOptions` 对象 `optionsOuter`,并设置其保存外部内容的选项。XmlOptions optionsOuter = new XmlOptions();optionsOuter.setSaveOuter();// 2.将追加文档对象 append 转换为XML字符串,并将结果存储在 appendString 变量中。String appendString = append.xmlText(optionsOuter);// 3.将源文档对象 src 转换为XML字符串,并将结果存储在 srcString 变量中。String srcString = src.xmlText();// 4.通过字符串操作,将源文档的XML内容分为四部分:prefix、mainPart、sufix 和 addPart,具体操作和含义与前述相同。String prefix = srcString.substring(0, srcString.indexOf(">") + 1);String mainPart = srcString.substring(srcString.indexOf(">") + 1, srcString.lastIndexOf("<"));String sufix = srcString.substring(srcString.lastIndexOf("<"));String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));// 5.如果 map 不为null且不为空,遍历 map 中的键值对,将 addPart 中的图片ID替换为对应的新ID。if (map != null && !map.isEmpty()) {// 对xml字符串中图片ID进行替换for (Map.Entry<String, String> set : map.entrySet()) {addPart = addPart.replace(set.getKey(), set.getValue());}}// 6.将 prefix、mainPart、addPart 和 sufix 拼接为一个完整的XML内容字符串,并通过 CTBody.Factory.parse() 方法将其解析为 CTBody 对象 makeBody。CTBody makeBody = CTBody.Factory.parse(prefix + mainPart + addPart + sufix);// 7.最后将合并后的 makeBody 设置为源文档对象 src 的内容。src.set(makeBody);}
}

3.下载工具类


import com.deepoove.poi.XWPFTemplate;
import com.test.entity.User;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.io.FileUtils;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/**下载工具类* */
public class ExportToWrodUtils {/*** 下载** @param response* @param sourceFilePath   源文件* @param moreFilePath     多个文件路径文件夹,template\\morelist\\* @param mergeFilePath    合并后文件路径,template\\res\\* @param finalFileName    合并后文件名字* @param downloadFileName 下载时的文件名字* @param userList* @throws Exception*/public static void downloadWord(HttpServletResponse response, String sourceFilePath, String moreFilePath, String mergeFilePath,String finalFileName, String downloadFileName, List<User> userList) throws Exception {// 1.获取文件流InputStream stream = new FileInputStream(sourceFilePath);// 2.数据列表XWPFTemplate template = XWPFTemplate.compile(stream);List<File> fileList = new ArrayList<>();for (User user : userList) {//可以改成自己的业务逻辑↓↓↓↓↓↓↓↓↓↓↓// 填充数据Map<String, String> data = new HashMap<>();data.put("name", user.getName());data.put("sex", user.getSex());// 根据身份证号或去年月日data.put("year", extractYearMonthDayOfIdCard(user.getCardId()).split("-")[0]);data.put("month", extractYearMonthDayOfIdCard(user.getCardId()).split("-")[1]);data.put("day", extractYearMonthDayOfIdCard(user.getCardId()).split("-")[2]);template.render(data);File file = new File(moreFilePath + user.getCardId() + ".docx");//可以改成自己的业务逻辑↑↑↑↑↑↑↑↑↑↑↑fileList.add(file);// 保存为单个Word文档FileOutputStream out = new FileOutputStream(file);template.write(out);out.close();}// 3.合并多个word,为一个wordMergeWordUtil.mergeMroeWord(fileList, mergeFilePath + finalFileName);// 4.删除合并后之前用到的多个单独word文件// 创建一个File对象,表示文件夹路径File folder = new File(moreFilePath);// 获取文件夹中的所有文件File[] files = folder.listFiles();// 遍历文件数组,删除Word文件for (File file : files) {if (file.isFile() && file.getName().endsWith(".docx")) {try {FileUtils.forceDelete(file);System.out.println("删除文件 " + file.getName());} catch (Exception e) {System.out.println("删除文件失败: " + file.getName());e.printStackTrace();}}}// 5.下载操作File file = null;FileInputStream is = null;try {response.setContentType("text/html;charset=utf-8");response.setCharacterEncoding("UTF-8");// 下载文件时的文件名字response.setHeader("content-disposition", "attachment;filename=\"" + URLEncoder.encode(downloadFileName + ".docx", "utf-8") + "\"");// 要下载的目标文件file = new File(mergeFilePath + finalFileName);is = new FileInputStream(file);ServletOutputStream os = response.getOutputStream();IOUtils.copy(is, os);} catch (IOException e) {e.printStackTrace();} finally {if (is != null) {is.close();}// if (file != null) {//     file.delete();// }}}/*** 省份证的正则表达式^(\d{15}|\d{17}[\dx])$ 方法类** @param id 省份证号* @return 生日(yyyy - MM - dd)*/public static String extractYearMonthDayOfIdCard(String id) {String year = null;String month = null;String day = null;// 正则匹配身份证号是否是正确的,15位或者17位数字+数字/x/Xif (id.matches("^\\d{15}|\\d{17}[\\dxX]$")) {year = id.substring(6, 10);month = id.substring(10, 12);day = id.substring(12, 14);} else {System.out.println("身份证号码不匹配!");return null;}return year + "-" + month + "-" + day;}}

四、业务逻辑使用工具类

 // 1.controller层/*** 导出为word数据列表** @throws IOException*/@GetMapping("/downloadWord")public void downloadWord(HttpServletResponse response) throws Exception {userService.downloadWord(response);}// 2.service层/*** 导出为word数据列表** @return* @throws IOException*/void downloadWord(HttpServletResponse response) throws Exception;// 3.serviceImpl层/*** 导出为word数据列表** @return* @throws IOException*/@Overridepublic void downloadWord(HttpServletResponse response) throws Exception {// 数据列表QueryWrapper queryWrapper = QueryWrapper.create().where(USER.POWER.eq("用户"));List<User> userList = userMapper.selectListWithRelationsByQuery(queryWrapper);ExportToWrodUtils.downloadWord(response, "template\\template2.docx", "template\\morelist\\", "template\\res\\", "合并后数据", "word数据列表", userList);}


五、调用接口,查看效果

浏览器直接get请求

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

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

相关文章

商城数据库88张表结构(九)

DDL 33.商品举报表 CREATE TABLE wang_informs (informid int(11) NOT NULL AUTO_INCREMENT COMMENT 自增ID,informTargetid int(11) NOT NULL COMMENT 举报人ID,goodid int(11) NOT NULL COMMENT 被举报商品ID,shopid int(11) NOT NULL COMMENT 被举报店铺ID,informType int(…

如何申请免费SSL证书,把网站升级成HTTPS

HTTPS&#xff08;Hyper Text Transfer Protocol Secure&#xff09;是一种用于安全数据传输的网络协议&#xff0c;它可以有效地保护网站和用户之间的通信安全。然而&#xff0c;要使一个网站从HTTP升级到HTTPS&#xff0c;就需要一个SSL证书。那么&#xff0c;如何申请免费的…

一文解读 SQL 生成工具

SQL 生成工具可用于测试 Parser 与其他数据库产品的兼容性&#xff0c;通过解析 YACC 语法文件中的产生式&#xff0c;生成对应的 SQL 语句&#xff0c;再使用数据库执行该 SQL&#xff0c;根据结果判断语句是否与其他数据库语法兼容。 01工具使用 语法文件预处理 预处理目的…

notion使用小tip(待补充)

可以替代思维导图是一个很棒的软件 公式编辑&#xff1a;latex 网站链接&#xff1a;LATEX语法 一些常用的用法&#xff1a; 下标&#xff1a;a_{Si} 分数&#xff1a;\frac{}{} 乘&#xff1a;\times 向量&#xff1a;\vec{} pai (3.14159…) : \pi 直接用公式编辑器&#…

MATLAB使用贝叶斯网络bnt工具箱:使用手册详解

MATLAB构建贝叶斯网络bnt工具箱: 贝叶斯网络(Bayesian networks)相关网页资源介绍1 工具包安装2 创建贝叶斯网络2.1 图结构(Graph structure)2.2 创建贝叶斯网络外壳(Creating the Bayes net shell)2.3 创建贝叶斯网络2.3 参数2.4 随机参数2 推断(Inference)2.1 边缘分…

【kettle005】kettle访问Oracle数据库并处理数据至execl文件(已更新)

1.一直以来想写下基于kettle的系列文章&#xff0c;作为较火的数据ETL工具&#xff0c;也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下Oracle数据库相关知识体系 3.欢迎批评指正&#xff0c;跪谢一键三连&am…

全栈从0到1 3D旅游地图标记和轨迹生成

功能演示 演示视频 体验地址 Vercel App 开发技术栈&#xff1a; NextJs&#xff08;前端框架&#xff09;React&#xff08;前端框架&#xff09;TailwindCSS &#xff08;CSS样式&#xff09;echart echart gl &#xff08;地图生成&#xff09;shadui&#xff08;UI组件…

Nginx自定义状态码499出现原因

499状态码定义 维基百科的定义 499 Client Closed Request (Nginx) Used in Nginx logs to indicate when the connection has been closed by client while the server is still processing itsrequest, making server unable to send a status code back 499状态码是nginx自…

Vue2 和 Vue3 的区别 (性能,编码方式,API 特性,源码)

在这个快节奏的时代&#xff0c;技术更新换代的速度也越来越快。我一直在使用Vue 2来开发项目。然而&#xff0c;现在越来越多的公司转向了Vue 3&#xff0c;并将其作为主要的前端开发框架。这就需要我们及时跟进新技术的发展&#xff0c;以保持竞争力。因此&#xff0c;我决定…

C语言阶段性测试错题纠正与拓展

引言&#xff1a;在2024年4月26日&#xff0c;我进行了C语言知识的“期末考试”。通过这次考试&#xff0c;我发现了我的知识漏洞。所以&#xff0c;我写下这篇博客来记录我的错题&#xff0c;并进行纠正&#xff0c;然后对于以前遗忘知识的回顾。 更多有关C语言的知识详解可前…

HarmonyOS编程实践系列:第一节 - 创建健康App欢迎页

系列文章目录 &#xff08;零&#xff09;鸿蒙HarmonyOS入门&#xff1a;如何配置环境&#xff0c;输出“Hello World“ &#xff08;一&#xff09;鸿蒙HarmonyOS开发基础 &#xff08;二&#xff09;鸿蒙HarmonyOS主力开发语言ArkTS-基本语法 &#xff08;三&#xff09;鸿蒙…

梳理一下低代码的真正价值!另推荐超好用的5款低代码开发平台

一、先来聊聊低代码的真实价值&#xff01; 在回答这个问题之前&#xff0c;我们不妨先来看两个案例&#xff1a; 某连锁商超企业在发展中产生了新的业务需求&#xff0c;一是希望能够快速展示门店销售数据&#xff0c;满足高层的管理需求&#xff1b;二是希望巡店、上架商品…

字典及GitHub字典爬取工具

红队API接口Fuzz字典可以用于WEB安全&#xff0c;渗透测试&#xff0c;SRC等场景 完整文件已上传知识星球&#xff0c;需要的朋友可加入查看。

Jitter 和相位噪声

近期学习PLL的时候&#xff0c;发现里面有诸多jitter类型&#xff0c;绕晕了&#xff0c;写个帖子记录一下学习过程。 目录 基本jitter和相噪概念 jitter 相位噪声 相位噪声和jitter之间的联系 三种常见的抖动类型及其特点 基本jitter和相噪概念 无噪声振荡器的输出是一…

状态模式

文章目录 1.UML类图2.状态基类3.状态实现类3.状态机管理类使用示例 1.UML类图 2.状态基类 public abstract class State {public string? Name { get; set; }public StateMachine? StateMachine {get; set;}public abstract void Exit();public abstract void Enter(); }3.…

生成删除数据库表数据的SQL语句

--获取表的名称及用表中第一列的注释作表的说明 select delete from ,Name2 ,--,RANK()OVER (ORDER BY Name2 DESC) as 序号,字段说明 from ( select bb.name as Name2,cc.* from ( select min(minor_id) as minor_id,id from ( SELECT g.minor_id minor_id,a.Name as Name …

解析vue.config.js文件

一、用途 创建 Vue 项目时&#xff0c;默认情况下是没有 vue.config.js 文件的。Vue CLI 会提供一组默认的配置&#xff0c;用于构建和开发项目&#xff0c;这些配置在内部被封装好了&#xff0c;并不需要用户手动创建 vue.config.js 文件来进行配置。通过在项目根目录下创建 …

重生奇迹MU首饰属性之迷

雷&#xff1a;抵抗移位、掌心雷 冰&#xff1a;抵抗冰度冻、冰封问箭、暴风雪、冰封 毒&#xff1a;降低中毒几率&#xff08;中毒不掉血&#xff09;、毒咒、毒炎 风&#xff1a;抵抗移位旋风斩&#xff08;没试过不过很多人用&#xff09;、龙卷风 至于火水地因为并没有…

分布式与一致性协议之Paxos算法(三)

Paxos算法 兰伯特关于Multi-Paxos的思考 领导者 我们可以通过引入领导者(Leader)节点来解决第一个问题。也就是说将领导者节点作为唯一提议者&#xff0c;如图所示。这样就不存在多个提议者同时提交提案的情况&#xff0c;也就不存在提案冲突的情况了。这里补充一点:在论文中…

NAT网络地址转换实验(思科)

华为设备参考&#xff1a;NAT网络地址转换实验&#xff08;华为&#xff09; 一&#xff0c;技术简介 NAT&#xff08;Network Address Translation&#xff09;&#xff0c;即网络地址转换技术&#xff0c;是一种在现代计算机网络中广泛应用的技术&#xff0c;主要用于有效管…