在Java中使用Apache POI保留Excel样式合并多个工作簿
背景
在日常工作中,我们经常需要将多个Excel文件合并成一个,同时保留原有的样式和格式。Apache POI是一个流行的Java库,用于读取和写入Microsoft Office格式的文件,包括Excel。然而,仅仅使用Apache POI的基本功能进行合并操作往往会导致样式的丢失,这对于需要保留格式信息的场景来说是不够的。常见的应用场景如钉钉的打卡数据只能一个一个的表格导出,接下来的文章就恰好可以运用到这样的场景中。
本文将探讨如何在合并Excel文件时保留原表格的样式,并提供一个改进的代码示例,以便更好地处理这个问题。
问题描述
在合并多个Excel文件时,常见的挑战之一是保留每个文件中的单元格样式。这些样式可能包括字体、颜色、边框、填充等。使用Apache POI的基本方法复制单元格时,只会复制内容,而不会保留这些样式。
解决方案
要解决这个问题,我们需要使用Apache POI的更高级功能来复制单元格样式。这涉及到创建样式映射,以便在合并过程中跟踪和复制每个单元格的样式。
以下是实现这一功能的关键步骤:
创建样式映射:使用一个映射结构(如HashMap)来存储从原始工作簿的CellStyle到合并工作簿的XSSFCellStyle的映射。这可以确保我们不会为相同的样式重复创建新的XSSFCellStyle对象。
遍历工作簿和工作表:对于要合并的每个Excel文件,遍历其中的每个工作簿和工作表。
复制行和单元格:对于每个工作表中的行和单元格,创建新的行和单元格,并复制内容。
复制样式:在复制单元格时,检查样式映射是否已经包含了该单元格的样式。如果没有,从原始工作簿中克隆样式,并将其添加到映射中。然后,将新克隆的样式应用到新创建的单元格上。
写入合并后的工作簿:最后,将合并后的工作簿写入输出文件。
代码示例
下面是一个示例代码,展示了如何实现上述解决方案:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.0</version>
</dependency>
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.0</version>
</dependency>
案例一:多个excel合成一个表格
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.*;
import java.util.ArrayList;
import java.util.List;public class ExcelMerger {public static void main(String[] args) {List<String> fileNames = new ArrayList<>();fileNames.add("H:\\time\\_打卡时间表1.xlsx");fileNames.add("H:\\time\\_打卡时间表2.xlsx");fileNames.add("H:\\time\\_打卡时间表3.xlsx");// 添加更多文件路径...String outputFileName = "H:\\time\\merged_excel_file.xlsx";try{InputStream inputStream;Workbook workbook;OutputStream outputStream = new FileOutputStream(outputFileName);XSSFWorkbook mergedWorkbook = new XSSFWorkbook();int j = 0;for (String fileName : fileNames) {inputStream = new FileInputStream(fileName);workbook = WorkbookFactory.create(inputStream);j ++;for (int i = 0; i < workbook.getNumberOfSheets(); i++) {Sheet sheet = workbook.getSheetAt(i);try {mergedWorkbook.createSheet(sheet.getSheetName() + i + j);}catch (Exception e){System.out.println(1);}copySheet(mergedWorkbook.getSheet(sheet.getSheetName() + i + j), sheet);}}mergedWorkbook.write(outputStream);} catch (IOException e) {e.printStackTrace();}}private static void copySheet(Sheet newSheet, Sheet sheet) {int rowCount = sheet.getLastRowNum() - sheet.getFirstRowNum();for (int i = 0; i <= rowCount; i++) {Row newRow = newSheet.createRow(i);Row currentRow = sheet.getRow(i);copyRow(newRow, currentRow);}}private static void copyRow(Row newRow, Row currentRow) {if (currentRow == null) {return;}int cellCount = currentRow.getLastCellNum();for (int i = 0; i < cellCount; i++) {Cell currentCell = currentRow.getCell(i);if (currentCell == null) {continue;}Cell newCell = newRow.createCell(i);copyCell(newCell, currentCell);}}private static void copyCell(Cell newCell, Cell currentCell) {newCell.setCellType(currentCell.getCellType());switch (currentCell.getCellType()) {case STRING:newCell.setCellValue(currentCell.getStringCellValue());break;case BOOLEAN:newCell.setCellValue(currentCell.getBooleanCellValue());break;case NUMERIC:newCell.setCellValue(currentCell.getNumericCellValue());break;case FORMULA:newCell.setCellFormula(currentCell.getCellFormula());break;default:break;}}
}
案例二:多个表格合成一个sheet
//初始化一个XSSFWorkbook实例作为合并后的工作簿,并创建一个名为"Merged Sheet"的新sheet。
//遍历每个要合并的Excel文件,并对每个文件中的每个sheet进行迭代。
//对于每个sheet中的每一行,都创建一个新的行并复制到合并后的sheet中。
//在复制行时,同时复制每个单元格的内容,包括字符串、布尔值、数字和公式。
//最后,将合并后的工作簿写入输出文件。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.*;
import java.util.ArrayList;
import java.util.List; public class ExcelMerger {public static void main(String[] args) { List<String> fileNames = new ArrayList<>();fileNames.add("H:\\time\\_打卡时间表1.xlsx");fileNames.add("H:\\time\\_打卡时间表2.xlsx");fileNames.add("H:\\time\\_打卡时间表3.xlsx");// 添加更多文件路径...String outputFileName = "H:\\time\\merged_excel_file.xlsx";try {InputStream inputStream; Workbook workbook;FileOutputStream outputStream = new FileOutputStream(outputFileName);XSSFWorkbook mergedWorkbook = new XSSFWorkbook(); Sheet mergedSheet = mergedWorkbook.createSheet("Merged Sheet"); int rowNum = 0; // 用于新sheet中的行号 for (String fileName : fileNames) { inputStream = new FileInputStream(fileName); workbook = WorkbookFactory.create(inputStream); for (int i = 0; i < workbook.getNumberOfSheets(); i++) { Sheet sheet = workbook.getSheetAt(i); for (Row row : sheet) { Row newRow = mergedSheet.createRow(rowNum++); copyRow(newRow, row); } } } mergedWorkbook.write(outputStream); } catch (IOException e) { e.printStackTrace(); } } private static void copyRow(Row newRow, Row currentRow) { if (currentRow == null) { return; } for (Cell cell : currentRow) { Cell newCell = newRow.createCell(cell.getColumnIndex(), cell.getCellType()); switch (cell.getCellType()) { case STRING: newCell.setCellValue(cell.getStringCellValue()); break; case BOOLEAN: newCell.setCellValue(cell.getBooleanCellValue()); break; case NUMERIC: newCell.setCellValue(cell.getNumericCellValue()); break; case FORMULA: newCell.setCellFormula(cell.getCellFormula()); break; case BLANK: // Handle blank cells as needed break; default: break; } } }
}
案例三:多个表格合成一个sheet,并保留样式
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class ExcelMergerWithStyles { public static void main(String[] args) { List<String> fileNames = new ArrayList<>();fileNames.add("H:\\time\\_打卡时间表1.xlsx");fileNames.add("H:\\time\\_打卡时间表2.xlsx");fileNames.add("H:\\time\\_打卡时间表3.xlsx");// 添加更多文件路径...String outputFileName = "H:\\time\\merged_excel_file.xlsx";try {InputStream inputStream;Workbook workbook;FileOutputStream outputStream = new FileOutputStream(outputFileName);XSSFWorkbook mergedWorkbook = new XSSFWorkbook(); Sheet mergedSheet = mergedWorkbook.createSheet("Merged Sheet"); int rowNum = 0; // 用于新sheet中的行号 // 用于存储已创建样式的映射,以避免重复创建相同的样式 Map<CellStyle, XSSFCellStyle> styleMap = new HashMap<>();for (String fileName : fileNames) { inputStream = new FileInputStream(fileName); workbook = WorkbookFactory.create(inputStream); for (int i = 0; i < workbook.getNumberOfSheets(); i++) { Sheet sheet = workbook.getSheetAt(i); for (Row row : sheet) { Row newRow = mergedSheet.createRow(rowNum++); copyRowWithStyles(newRow, row, mergedWorkbook, styleMap); } } } mergedWorkbook.write(outputStream); } catch (IOException e) { e.printStackTrace(); } } private static void copyRowWithStyles(Row newRow, Row currentRow, XSSFWorkbook mergedWorkbook, Map<CellStyle, XSSFCellStyle> styleMap) { if (currentRow == null) { return; } for (Cell cell : currentRow) { Cell newCell = newRow.createCell(cell.getColumnIndex(), cell.getCellType()); copyCellContent(cell, newCell); // 复制样式 CellStyle cellStyle = cell.getCellStyle(); XSSFCellStyle newCellStyle = styleMap.get(cellStyle); if (newCellStyle == null) { newCellStyle = mergedWorkbook.createCellStyle(); newCellStyle.cloneStyleFrom(cellStyle); styleMap.put(cellStyle, newCellStyle); } newCell.setCellStyle(newCellStyle); } } private static void copyCellContent(Cell source, Cell target) { switch (source.getCellType()) { case STRING: target.setCellValue(source.getStringCellValue()); break; case BOOLEAN: target.setCellValue(source.getBooleanCellValue()); break; case NUMERIC: target.setCellValue(source.getNumericCellValue()); break; case FORMULA: target.setCellFormula(source.getCellFormula()); break; case BLANK: // Handle blank cells as needed break; default: break; } }
}
总结
通过扩展Apache POI库的基本功能,我们可以实现在合并Excel文件时保留原表格样式的能力。这涉及到创建样式映射,遍历工作簿和工作表,复制行、单元格和样式,并最终写入合并后的工作簿。虽然这个过程可能比简单的合并更为复杂,但它提供了更大的灵活性,允许我们根据需要定制合并后的Excel文件的样式和格式。
对于需要处理大量Excel文件或具有复杂样式的场景,这种方法可能需要进行进一步的优化和错误处理。但无论如何,它提供了一个很好的起点,用于在Java中实现具有样式保留功能的Excel合并操作。
相关文章:
在Java中使用Apache POI保留Excel样式合并多个工作簿
背景 在日常工作中,我们经常需要将多个Excel文件合并成一个,同时保留原有的样式和格式。Apache POI是一个流行的Java库,用于读取和写入Microsoft Office格式的文件,包括Excel。然而,仅仅使用Apache POI的基本功能进行…...
Nomachine远程黑屏通用处理方法
Nomachine远程黑屏通用处理方法 文章目录 前言正文解决步骤 总结 前言 NoMachine是一种远程桌面软件,它允许用户通过互联网或局域网连接到远程计算机,并在本地计算机上使用远程计算机的桌面环境和应用程序。它提供了高性能的图形渲染和低延迟的响应&…...

基于51单片机数控直流电压源proteus仿真LCD显示+程序+设计报告+讲解视频
基于51单片机数控直流电压源proteus仿真LCD显示( proteus仿真程序设计报告讲解视频) 仿真图proteus7.8及以上 程序编译器:keil 4/keil 5 编程语言:C语言 设计编号:S0072 讲解视频 基于51单片机数控直流电压源proteus仿真程序…...

[Linux]文件缓冲区
文件fd 输出重定向除了用dup2()改变数组下标外,还可以用命令来完成 所有的命令执行,都必须有操作系统将其运行起来变成进程,然后根据>>, <<来判断是输入重定向,还是输出重定向。 缓冲区 之所以有缓冲区࿰…...

ARM:按键中断
key_inc.c #include"key_inc.h"void key1_it_config(){//使能GPIOF外设时钟RCC->MP_AHB4ENSETR | (0x1<<5);//将PF9设置为输入模式GPIOF->MODER & (~(0x3<<18));//设置由PF9管脚产生EXTI9事件EXTI->EXTICR3 & (~(0XFF<<8));EXTI…...
JavaScript高级(五)--柯西化函数
柯里化 定义 首先,我们参照维基百科给出的定义: 在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数…...

带3090显卡的Linux服务器上部署SDWebui
背景 一直在研究文生图,之前一直是用原始模型和diffuser跑SD模型,近来看到不少比较博主在用 SDWebui,于是想着在Linux服务器上部署体验一下,谁知道并没有想象的那么顺利,还是踩了不少坑。记录一下过程,也许…...

37、Linux中Xsync数据同步备份工具
37、Linux中Xsync数据同步备份工具 一、介绍二、配置集群hostname三、修改xsync文件四、赋权五、安装Rsync六、验证一七、配置免密登录1、生成rsa密钥2、copy机器自身公钥到目标机器3、.ssh/文件目录赋权 八、验证二 ⚠️ 注:本文全程在普通用户下操作,…...
网络基础:构建你的数字世界之桥
I. 引言 A. 什么是网络? 网络是由各种电子设备通过通信线路或无线电波连接起来,以便彼此之间进行数据交换和资源共享的系统。在当今数字化时代,网络已经成为我们生活和工作中不可或缺的一部分,无论是互联网、局域网还是无线通信…...

Python 全栈系列236 rabbit_agent搭建
说明 通过rabbit_agent, 以接口方式实现对队列的标准操作,将pika包在微服务内,而不必在太多地方重复的去写。至少在服务端发布消息时,不必再去考虑这些问题。 在分布式任务的情况下,客户端本身会启动一个持续监听队列的客户端服…...

管理自由,体验简单,使用安全 | 详解威联通全套多用户多权限管理方案【附TS-466C产品介绍】
管理自由,体验简单,使用安全 | 详解威联通全套多用户多权限管理方案【附TS-466C产品介绍】 哈喽小伙伴们好,我是Stark-C~。今天我们来解决一个之前评论区多次被提及的问题--多用户权限管理。 对于我们NAS用户来说,基本都会面临这…...

【Redis】优惠券秒杀
全局唯一ID 全局唯一ID生成策略: UUIDRedis自增snowflake算法数据库自增 Redis自增ID策略:每天一个key,方便统计订单量ID构造是 时间戳 计数器 Component public class RedisIdWorker {// 2024的第一时刻private static final long BEGIN…...
【几何】平面方程
文章目录 平面方程一般式截距式点法式法线式 平面方程 平面方程是用一个方程来表示平面,平面上的所有点代入方程,方程都成立。因为用法的不同,平面方程一般有四种表现形式。 一般式 设 n ⃗ ( A , B , C ) \vec n(A,B,C) n (A,B,C) 为平…...

macOS访问samba文件夹的正确姿势,在哪里更改“macOS的连接身份“?还真不好找!
环境:路由器上需要身份认证的Mini NAS macOS Sonoma 14 这是一个非常简单的问题,但解决方法却藏得比较深,不够直观,GPT也没有给出明确的解决提示,特意记录一下。 macOS很多地方都很自动,有时候让人找不到设…...
linux进程切换
内核堆栈:每个进程在内核模式下运行时都有自己的内核堆栈。这个堆栈保存了进程在内核模式下的运行状态,包括函数调用时传递的参数、局部变量和返回地址等。 用户态与内核态:进程通常在用户态下运行,当执行系统调用或响应中断时进…...
spring boot 如何升级 Tomcat 版本
在Spring Boot应用程序中升级内嵌的Tomcat版本通常涉及以下几个步骤: 1. 确定当前使用的Tomcat版本 首先,你需要确定你的Spring Boot应用程序当前使用的Tomcat版本。这可以通过查看项目的pom.xml或build.gradle文件来完成,其中会列出所有的…...

sentinel中StatisticSlot数据采集的原理
StatisticSlot数据采集的原理 时间窗口 固定窗口 在固定的时间窗口内,可以允许固定数量的请求进入;超过数量就拒绝或者排队,等下一个时间段进入, 如下图 时间窗长度划分为1秒 单个时间窗的请求阈值为3 上述存在一个问题, 假如9:18:04:…...
图像去噪与增强技术
图像去噪与增强技术是数字图像处理领域中的两个重要方面,它们分别关注消除图像中的噪声和改善图像的质量。 图像去噪技术的主要目的是从受噪声干扰的图像中去除不必要的随机信号,以恢复图像的真实内容。这对于图像的进一步分析和理解至关重要。去噪技术包…...

SpringJPA 做分页条件查询
前言: 相信小伙伴们的项目很多都用到SpringJPA框架的吧,对于单表的增删改查利用jpa是很方便的,但是对于条件查询并且分页 是不是很多小伙伴不经常写到. 今天我整理了一下在这里分享一下. 话不多说直接上代码: Controller: RestController public class ProductInstanceContr…...

[Java基础揉碎]单例模式
目录 什么是设计模式 什么是单例模式 饿汉式与懒汉式 饿汉式vs懒汉式 懒汉式存在线程安全问题 什么是设计模式 1.静态方法和属性的经典使用 2.设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模式就像是经典的棋谱&am…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...