java操作doc——java利用Aspose.Words操作Word文档并动态设置单元格合并

embedded/2024/11/27 22:39:09/

在实际工作中,如果业务线是管理类项目或者存在大量报表需要导出的业务时,可以借助第三方插件实现其对应功能。

尤其是需要对word文档的动态操作或者模板数据的定向合并,使用Aspose会相对来说容易一些,而且相关文档比较完整,具体如何使用,请参看如下案例:

下图展示的是数据模板图1:

一、什么是Aspose?

官网地址:Aspose.Words 产品系列 | Aspose API 参考

文档处理是个人和专业人士必备的技能。然而,手动文档处理可能非常耗时且容易。Aspose.Words Java 文档处理教程提供了使用代码自动生成和管理文档的全面指南。

这些教程涵盖了广泛的主题,从基本的表格处理到高级文档合并和水印。Aspose.Words 教程注重实际实施,使开发人员能够高效地简化文档处理任务并提供定制的解决方案。

无论您是初学者还是经验丰富的开发人员,Aspose.Words Java 文档处理教程都可以帮助您将文档处理技能提升到更高的水平。

二、springboot项目如何引入?

1.获得许可

2.引入pom

java">        <dependency><groupId>aspose</groupId><artifactId>words</artifactId><version>21.4.0</version><classifier>jdk17</classifier></dependency>

 三、具体使用案例

1.集成授权

java">    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.设置模板绑定字段

java">    /*** 设置模板绑定字段** @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.配置动态表格,主要用于集合遍历(数据列表展示)

java">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.业务实现

java">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.单元格合并问题(仅展示垂直合并)

  • 解决不连续合并的问题(包括垂直和水平合并两种)
java">//默认的行数 = 固定表格数(从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);
}
  • 对于连续合并的模板(此方案必须基于保存的文件)
java">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.需要主要的问题点

  • 水平合并关键代码
java">documentBuilder.getCellFormat().setHorizontalMerge(CellMerge.FIRST);
  • 在编辑数据模板时需要切换域代码,也就是文中指定的数据模板图1

http://www.ppmy.cn/embedded/141020.html

相关文章

开发一套ERP 第三弹 前端构建

初步确定 差不多就套用 pnpm config set registry https://registry.npmmirror.com/ pnpm 配置国内镜像源

Docker 的优势和劣势分别是什么

Docker 在现代软件开发与部署领域意义非凡&#xff0c;有着多方面的优势。在环境一致性方面表现卓越&#xff0c;软件开发各环节常因环境差异产生兼容性故障&#xff0c;而 Docker 把应用及其依赖封装成容器&#xff0c;无论在哪运行&#xff0c;环境都相同&#xff0c;大大减少…

Web 学习笔记 - 网络安全

前言 作为 前端开发者&#xff0c;了解一点 Web 安全方面的基本知识是有很必要的&#xff0c;未必就要深入理解。本文主要介绍常见的网络攻击类型&#xff0c;不作深入探讨。 正文 网络攻击的形式种类繁多&#xff0c;从简单的网站敏感文件扫描、弱口令暴力破解&#xff0c;…

面试学习准备

根据面试题web前端面试 - 面试官系列 里面的题目学习巩固。 1.vue2 组件通信 EventBus&#xff1a; 讲解 全局事件总线&#xff0c;核心思想是通过发布-订阅模式来实现组件之间的通信 在 Vue 2 中&#xff0c;可以直接使用 Vue 实例作为 EventBus。 使用方法&#xff1a;在…

C++设计模式-策略模式-StrategyMethod

动机&#xff08;Motivation&#xff09; 在软件构建过程中&#xff0c;某些对象使用的算法可能多种多样&#xff0c;经常改变&#xff0c;如果将这些算法都编码到对象中&#xff0c;将会使对象变得异常复杂&#xff1b;而且有时候支持不使用的算法也是一个性能负担。 如何在运…

UE4 iOS Package的过程与XCode工程中没有游戏Content的原因

疑问 我观察XCode工程时&#xff0c;没有看到包含游戏Content或Content的Pak&#xff0c;为此研究代码 Package流程示意图 下面是若干步骤的解释&#xff1a; &#xff08;40&#xff09;日志见&#xff1a; 文档&#xff1a;子页&#xff1a; IOSPlatform.PrepForUATPackag…

8K超高清相机+双光谱融合技术

8K超高清相机 双光谱融合技术

C语言超详细教程

系列文章目录 文章目录 系列文章目录1 运算符1.1 算术运算符:2 控制语句2.1 条件语句:2.2 循环语句:3 函数3.1 函数的定义与声明:3.2 递归函数:4 指针4.1 指针的定义与使用函数指针:5. 数组与字符串5.1 数组一维数组:相同类型元素的集合(如:多维数组:数组的数组(如:…