java操作doc——java利用Aspose.Words操作Word文档并动态设置单元格合并
在实际工作中,如果业务线是管理类项目或者存在大量报表需要导出的业务时,可以借助第三方插件实现其对应功能。
尤其是需要对word文档的动态操作或者模板数据的定向合并,使用Aspose会相对来说容易一些,而且相关文档比较完整,具体如何使用,请参看如下案例:
下图展示的是数据模板图1:
一、什么是Aspose?
官网地址:Aspose.Words 产品系列 | Aspose API 参考
文档处理是个人和专业人士必备的技能。然而,手动文档处理可能非常耗时且容易。Aspose.Words Java 文档处理教程提供了使用代码自动生成和管理文档的全面指南。
这些教程涵盖了广泛的主题,从基本的表格处理到高级文档合并和水印。Aspose.Words 教程注重实际实施,使开发人员能够高效地简化文档处理任务并提供定制的解决方案。
无论您是初学者还是经验丰富的开发人员,Aspose.Words Java 文档处理教程都可以帮助您将文档处理技能提升到更高的水平。
二、springboot项目如何引入?
1.获得许可
2.引入pom
<dependency><groupId>aspose</groupId><artifactId>words</artifactId><version>21.4.0</version><classifier>jdk17</classifier></dependency>
三、具体使用案例
1.集成授权
public static void setAuthLicense() {try {//根目录下的授权文件ClassPathResource resource = new ClassPathResource("授权文件路径");URL licenseFileNameURL = resource.getUrl();String savePath = java.net.URLDecoder.decode(licenseFileNameURL.toString(), "utf-8");String licenseFileName = savePath.toString().substring(6);if (new File(licenseFileName).exists()) {License license = new License();license.setLicense(licenseFileName);}} catch (Exception e) {e.printStackTrace();log.error("aspose 授权异常");}}
2.设置模板绑定字段
/*** 设置模板绑定字段** @param document 默认的文档对象* @param fieldname 模板key* @param fieldvalue 模板value* @throws Exception*/public static void mergeField(Document document, String fieldname,Object fieldvalue) throws Exception {document.getMailMerge().execute(new String[]{fieldname},new Object[]{fieldvalue});}
3.配置动态表格,主要用于集合遍历(数据列表展示)
public class ResultSetDataSource {public ResultSetDataSource(){}/*** 处理自定义的数据源* @param document 文档* @param tableStartFieldName tableStart的域名* @param fieldNames 列的域名数组* @param fieldValueArray 域值的数组* @throws Exception*/public void executeWithRegions(Document document, String tableStartFieldName, String[] fieldNames, String[][] fieldValueArray) throws Exception {java.sql.ResultSet resultSet = createCachedRowSet(fieldNames);int length = fieldValueArray.length;for(int i = 0; i < length;i ++){addRow(resultSet,fieldValueArray[i]);}com.aspose.words.net.System.Data.DataTable table = new com.aspose.words.net.System.Data.DataTable(resultSet, tableStartFieldName);document.getMailMerge().executeWithRegions(table);}public java.sql.ResultSet createCachedRowSet(String[] columnNames) throws Exception{RowSetMetaDataImpl metaData = new RowSetMetaDataImpl();metaData.setColumnCount(columnNames.length);for (int i = 0; i < columnNames.length; i++){metaData.setColumnName(i + 1, columnNames[i]);metaData.setColumnType(i + 1, java.sql.Types.VARCHAR);}CachedRowSet rowSet = RowSetProvider.newFactory().createCachedRowSet();rowSet.setMetaData(metaData);return rowSet;}public void addRow(java.sql.ResultSet resultSet, String[] values) throws Exception{resultSet.moveToInsertRow();for (int i = 0; i < values.length; i++) {resultSet.updateString(i + 1, values[i]);}resultSet.insertRow();resultSet.moveToCurrentRow();resultSet.last();}
4.业务实现
public void testExportDoc(@RequestParam("id") String testId, HttpServletResponse response){//aspose 授权AsposeWordLib.setAuthLicense();SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");//插入动态表格ResultSetDataSource dataSource = new ResultSetDataSource();OutputStream responseOutputStream = null;try{//基础数据赋值String uuid = testId;RequestLogUtils.setAttribute("code",uuid);ClassPathResource pathResource = new ClassPathResource("test01/test01.docx");InputStream in = pathResource.getInputStream();Document doc = new Document(in);WordLib.mergeField(doc, "code",uuid);WordLib.mergeField(doc, "productmodel", "2024-M11-");WordLib.mergeField(doc, "productname", "产品名称");WordLib.mergeField(doc, "username", "用户名");Date date = new Date();WordLib.mergeField(doc, "applytimestr",formatter.format(date));WordLib.mergeField(doc, "num",556251);//集合数据动态赋值String [] arrDefault = {"seq","name","sign"};String [][] arrayTab = new String[2][3];for (int i = 0; i < 2; i++) {arrayTab[i][0] = i + "";arrayTab[i][1] = "物资" + i;arrayTab[i][2] = "sign" + i;}dataSource.executeWithRegions(doc,"t1",arrDefault, arrayTab);String [] arrMerge = {"content","status","userStr"};String [][] arrayTabMerge = new String[3][3];for (int i = 0; i < 3; i++) {arrayTabMerge[i][0] = i + "、内容" + i;if(i%2 == 0){arrayTabMerge[i][1] = "☑ 正常 ☐ 停用";}else {arrayTabMerge[i][1] = "☐ 正常 ☑ 停用";}arrayTabMerge[i][2] = "用户";}dataSource.executeWithRegions(doc,"t2",arrMerge, arrayTabMerge);//默认的行数 = 固定表格数(从1开始计算)+动态计算的arrayTab长度int defaultRow = 7 + arrayTab.length;//需要合并的行数int dynamicMergeRowsCM = arrayTabMerge.length;//内容列表-详细内容单元格合并DocumentBuilder documentBuilder = new DocumentBuilder(doc);//移动到第一个表格的第defaultStaIndex行的第columnIndex列(第一个格子)的第一个字符(doc表格外的标题不做计算)documentBuilder.moveToCell(0, defaultRow, 0, 0);documentBuilder.getCellFormat().setVerticalMerge(CellMerge.FIRST);for(int i = 0; i < dynamicMergeRowsCM; i++ ) {documentBuilder.moveToCell(0, defaultRow + i, 0, 0);documentBuilder.getCellFormat().setVerticalMerge(CellMerge.PREVIOUS);}//内容列表-操作人单元格合并//移动到第一个表格的第defaultStaIndex行的第columnIndex列(第一个格子)的第一个字符(doc表格外的标题不做计算)documentBuilder.moveToCell(0, defaultRow, 3, 0);documentBuilder.getCellFormat().setVerticalMerge(CellMerge.FIRST);for(int i = 0; i < dynamicMergeRowsCM; i++ ) {documentBuilder.moveToCell(0, defaultRow + i, 3, 0);documentBuilder.getCellFormat().setVerticalMerge(CellMerge.PREVIOUS);}//返回前台ByteArrayOutputStream outputStream = new ByteArrayOutputStream();try {doc.save(outputStream, SaveFormat.DOCX);} catch (Exception e) {e.printStackTrace();}// 建立一个文件的输出的输出流ByteArrayOutputStream byteArrayOutputStream = (ByteArrayOutputStream) outputStream;byte[] aByte = byteArrayOutputStream.toByteArray();response.setCharacterEncoding("UTF-8");response.setHeader("Content-Disposition", "attachment; filename=" + uuid +".docx");String returnType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";response.setContentType(returnType);responseOutputStream = response.getOutputStream();responseOutputStream.write(aByte);responseOutputStream.flush();}catch (Exception e){e.printStackTrace();}}
5.单元格合并问题(仅展示垂直合并)
- 解决不连续合并的问题(包括垂直和水平合并两种)
//默认的行数 = 固定表格数(从1开始计算)+动态计算的arrayTab长度
int defaultRow = 7 + arrayTab.length;
//需要合并的行数
int dynamicMergeRowsCM = arrayTabMerge.length;
//内容列表-详细内容单元格合并
DocumentBuilder documentBuilder = new DocumentBuilder(doc);
//移动到第一个表格的第defaultStaIndex行的第columnIndex列(第一个格子)的第一个字符(doc表格外的标题不做计算)
documentBuilder.moveToCell(0, defaultRow, 0, 0);
documentBuilder.getCellFormat().setVerticalMerge(CellMerge.FIRST);
for(int i = 0; i < dynamicMergeRowsCM; i++ ) {documentBuilder.moveToCell(0, defaultRow + i, 0, 0);documentBuilder.getCellFormat().setVerticalMerge(CellMerge.PREVIOUS);
}
- 对于连续合并的模板(此方案必须基于保存的文件)
public static void mergeCells(Cell startCell, Cell endCell) {Table parentTable = startCell.getParentRow().getParentTable();Point startCellPos = new Point(startCell.getParentRow().indexOf(startCell), parentTable.indexOf(startCell.getParentRow()));Point endCellPos = new Point(endCell.getParentRow().indexOf(endCell), parentTable.indexOf(endCell.getParentRow()));Rectangle mergeRange = new Rectangle(Math.min(startCellPos.x, endCellPos.x), Math.min(startCellPos.y, endCellPos.y), Math.abs(endCellPos.x - startCellPos.x) + 1,Math.abs(endCellPos.y - startCellPos.y) + 1);for (Row row : parentTable.getRows()) {for (Cell cell : row.getCells()) {Point currentPos = new Point(row.indexOf(cell), parentTable.indexOf(row));if (mergeRange.contains(currentPos)) {if (currentPos.x == mergeRange.x)cell.getCellFormat().setHorizontalMerge(CellMerge.FIRST);elsecell.getCellFormat().setHorizontalMerge(CellMerge.PREVIOUS);if (currentPos.y == mergeRange.y)cell.getCellFormat().setVerticalMerge(CellMerge.FIRST);elsecell.getCellFormat().setVerticalMerge(CellMerge.PREVIOUS);}}}}//具体实现方案
public static void main(String[] args) {ClassPathResource re = new ClassPathResource("exportDocTemplate/new241119/test02.docx");InputStream in = re.getInputStream();Document targetDoc = new Document(in);Table table = targetDoc.getFirstSection().getBody().getTables().get(0);Cell cellStartRange = table.getRows().get(12).getCells().get(0); //开始的行和列Cell cellEndRange = table.getRows().get(13).getCells().get(0); //结束的行列ReportExportCommonUtils.mergeCells(cellStartRange, cellEndRange);Cell s1 = table.getRows().get(12).getCells().get(3); //开始的行和列Cell e1 = table.getRows().get(13).getCells().get(3); //结束的行列ReportExportCommonUtils.mergeCells(s1, e1);Cell s2 = table.getRows().get(10).getCells().get(0); //开始的行和列Cell e2 = table.getRows().get(11).getCells().get(0); //结束的行列ReportExportCommonUtils.mergeCells(s2, e2);Cell s3 = table.getRows().get(10).getCells().get(3); //开始的行和列Cell e3 = table.getRows().get(11).getCells().get(3); //结束的行列mergeCells(s3, e3);}
6.最终效果展示
7.需要主要的问题点
- 水平合并关键代码
documentBuilder.getCellFormat().setHorizontalMerge(CellMerge.FIRST);
- 在编辑数据模板时需要切换域代码,也就是文中指定的数据模板图1
相关文章:

java操作doc——java利用Aspose.Words操作Word文档并动态设置单元格合并
在实际工作中,如果业务线是管理类项目或者存在大量报表需要导出的业务时,可以借助第三方插件实现其对应功能。 尤其是需要对word文档的动态操作或者模板数据的定向合并,使用Aspose会相对来说容易一些,而且相关文档比较完整&#…...

探索 .NET 9 控制台应用中的 LiteDB 异步 CRUD 操作
本文主要是使用异步方式,体验 litedb 基本的 crud 操作。 LiteDB 是一款轻量级、快速且免费的 .NET NoSQL 嵌入式数据库,专为小型本地应用程序设计。它以单一数据文件的形式提供服务,支持文档存储和查询功能,适用于桌面应用、移动…...
《进程隔离机制:C++多进程编程安全的坚固堡垒》
在当今数字化时代,软件系统的安全性愈发成为人们关注的焦点。尤其是在 C多进程编程领域,如何确保进程间的安全交互与数据保护,是每一位开发者都必须面对的重要课题。而进程隔离机制,犹如一座坚固的堡垒,为 C多进程编程…...
构建无障碍的数字世界:深入探讨Web可访问性指南
文章目录 前言一、什么是Web可访问性?二、Web内容无障碍指南(WCAG)三、ARIA属性的应用四、实践中的Web可访问性结语 前言 在当今高度互联的世界里,互联网已成为人们日常生活不可或缺的一部分。然而,对于数百万残障人士…...

跨境出海安全:如何防止PayPal账户被风控?
今天咱们聊聊那些让人头疼的事儿——PayPal账户被风控。不少跨境电商商家反馈,我们只是想要安安静静地在网上做个小生意,结果不知道为什么,莫名其妙账户就被冻结了。 但其实每个封禁都是有原因的,今天就来给大家分享分享可能的原…...

学习日记_20241123_聚类方法(MeanShift)
前言 提醒: 文章内容为方便作者自己后日复习与查阅而进行的书写与发布,其中引用内容都会使用链接表明出处(如有侵权问题,请及时联系)。 其中内容多为一次书写,缺少检查与订正,如有问题或其他拓展…...
AI编程和AI绘画哪个更适合创业?
AI编程和AI绘画各有优势,适合创业的方向取决于你的资源、兴趣、市场需求和技术能力。以下是两者的对比分析,帮助你选择更适合的方向: AI编程 优势 1、广泛应用领域: 涉及自动化、数据分析、自然语言处理、计算机视觉等多个领域&a…...

macOS 无法安装第三方app,启用任何来源的方法
升级新版本 MacOS 后,安装下载的软件时,不能在 ”安全性与隐私” 中找不到 ”任何来源” 选项。 1. 允许展示任何来源 点击 启动器 (Launchpad) – 其他 (Other) – 终端 (Terminal): 打开终端后,输入以下代码回车: …...

关于SpringBoot集成Kafka
关于Kafka Apache Kafka 是一个分布式流处理平台,广泛用于构建实时数据管道和流应用。它能够处理大量的数据流,具有高吞吐量、可持久化存储、容错性和扩展性等特性。 Kafka一般用作实时数据流处理、消息队列、事件架构驱动等 Kafka的整体架构 ZooKeeper:…...

4.STM32之通信接口《精讲》之IIC通信---软件实现IIC《深入浅出》面试必备!
接下正式,进入软件编写IIC时序了,并实现对MPU6050的控制,既然是软件实现,那么硬件方面,我仅需两根控制线即可,即:数据控制线SDA,时钟控制线SCL。(人为软件层面定义的&…...
6G通信技术对比5G有哪些不同?
6G,即第六代移动通信技术,是5G之后的延伸,代表了一种全新的通信技术发展方向。与5G相比,6G在多个方面都有显著的不同和提升,以下是对6G通信技术及其与5G差异的详细分析: 一、6G的基本特点 更高的传输速率…...
「Mac玩转仓颉内测版28」基础篇8 - 元组类型详解
本篇将介绍 Cangjie 中的元组类型,包括元组的定义、创建、访问、数据解构以及应用场景,帮助开发者掌握元组类型的使用。 关键词 元组类型定义元组创建元组访问数据解构应用场景 一、元组类型概述 在 Cangjie 中,元组是一种用于存储多种数据…...

WebStorm 2024.3/IntelliJ IDEA 2024.3出现elementUI提示未知 HTML 标记、组件引用爆红等问题处理
WebStorm 2024.3/IntelliJ IDEA 2024.3出现elementUI提示未知 HTML 标记、组件引用爆红等问题处理 1. 标题识别elementUI组件爆红 这个原因是: 在官网说明里,才版本2024.1开始,默认启用的 Vue Language Server,但是在 Vue 2 项目…...

机械设计学习资料
免费送大家学习资源,已整理好,仅供学习 下载网址: https://www.zzhlszk.com/?qZ02-%E6%9C%BA%E6%A2%B0%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83SOP.zip...

Python 快速入门(上篇)❖ Python 字符串
Python 字符串 字符串格式化输出字符串拼接获取字符串长度字符串切片字符串处理方法 字符串格式化输出 name “xhx” age 30 # 方法1 print("我的名字是%s,今年%s岁了。 " % (name, age)) # 方法2 print(f"我的名字是{name},今年{age}岁了。")字符串拼接…...

Ubuntu中使用多版本的GCC
我的系统中已经安装了GCC11.4,在安装cuda时出现以下错误提示: 意思是当前的GCC版本过高,要在保留GCC11.4的同时安装GCC9并可以切换,可以通过以下步骤实现: 步骤 1: 安装 GCC 9 sudo apt-get update sudo apt-get ins…...

1+X应急响应(网络)文件包含漏洞:
常见网络攻击-文件包含漏洞&命令执行漏洞: 文件包含漏洞简介: 分析漏洞产生的原因: 四个函数: 产生漏洞的原因: 漏洞利用条件: 文件包含: 漏洞分类: 本地文件包含: …...
机器学习实战记录(1)
决策树——划分数据集 def splitDataSet(dataSet, axis, value): retDataSet [] #创建返回的数据集列表for featVec in dataSet: #遍历数据集if featVec[axis] value:reducedFeatVec featVec[:axis] #去掉axis特征reducedFeatVec.extend(featVec[axis1…...
PHP8解析php技术10个新特性
PHP8系列是 PHP编程语言的最新主线版本,带来了许多激动人心的新特性和改进。作为一名PHP开发者,了解这些更新能够帮助你编写更高效、安全和现代的代码。 8的核心技术知识点,包括语言特性、性能优化、安全增强以及开发者工具的改进。 Just-In…...
C++模版特化和偏特化
什么是模版特化 特化的含义:所谓特化,就是将泛型搞得具体化一些,从字面上来解释,就是为已有的模板参数进行一些使其特殊化的指定,使得以前不受任何约束的模板参数,或受到特定的修饰(例如const或…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...