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,一经查实,立即删除!

相关文章

如何申请免费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 直接用公式编辑器&#…

【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组件…

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 …

重生奇迹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;主要用于有效管…

游戏新手村23:游戏数据分析都是谁在看数据

不管是做端游页游还是手游&#xff0c;不管是做市场广告投放还是游戏运营&#xff0c;都需要看数据。有的人说“数据会说话”&#xff0c;也有人说“数据会说谎”&#xff0c;有的人言必谈大数据&#xff0c;有的人则能善于从细小的数据着手发现问题。 我知道和了解的一些游戏…

SpringBoot - java.lang.NoClassDefFoundError: XXX

问题描述 以 json-path 为例&#xff1a;java.lang.NoClassDefFoundError: com/jayway/jsonpath/Configuration 原因分析 编译不报错&#xff0c;但是运行时报错。 遇到这样类似的问题&#xff0c;首先就要想到是不是 Jar 包冲突引起的&#xff0c;或者引入的不是理想的 Jar…

数据仓库实验二:关联规则挖掘实验

目录 一、实验目的二、实验内容和要求三、实验步骤1、创建数据库和表2、挖掘关联规则&#xff08;1&#xff09;新建一个 Analysis Services 项目 Sales&#xff08;2&#xff09;建立数据源视图&#xff08;3&#xff09;建立挖掘结构 Sales.dmm&#xff08;4&#xff09;部署…

Java集合相关的List、Set、Map基础知识

目录 一、集合介绍 二、List 三、Map HashMap的数据结构 如何理解红黑树 四、set 一、集合介绍 在Java中&#xff0c;集合是一种用于存储对象的数据结构&#xff0c;它提供了一种更加灵活和强大的方式来处理和操作数据。Java集合框架提供了一系列接口和类&#xff0c;用…

Transformer模型详解01-Word Embedding

文章目录 前言Transformer 整体结构Transformer 的输入单词 Embedding原理CBOW 模型one-hot构建 CBOW 训练数据集构建 CBOW 神经网络训练 CBOW 神经网络 Skip-gram 模型one-hot构建 Skip-gram训练数据集训练 Skip-gram神经网络 Word2Vec实例数据训练保存和加载 前言 Transform…