当前位置: 首页 > news >正文

在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样式合并多个工作簿

背景 在日常工作中&#xff0c;我们经常需要将多个Excel文件合并成一个&#xff0c;同时保留原有的样式和格式。Apache POI是一个流行的Java库&#xff0c;用于读取和写入Microsoft Office格式的文件&#xff0c;包括Excel。然而&#xff0c;仅仅使用Apache POI的基本功能进行…...

Nomachine远程黑屏通用处理方法

Nomachine远程黑屏通用处理方法 文章目录 前言正文解决步骤 总结 前言 NoMachine是一种远程桌面软件&#xff0c;它允许用户通过互联网或局域网连接到远程计算机&#xff0c;并在本地计算机上使用远程计算机的桌面环境和应用程序。它提供了高性能的图形渲染和低延迟的响应&…...

基于51单片机数控直流电压源proteus仿真LCD显示+程序+设计报告+讲解视频

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

[Linux]文件缓冲区

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

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高级(五)--柯西化函数

柯里化 定义 首先&#xff0c;我们参照维基百科给出的定义&#xff1a; 在计算机科学中&#xff0c;柯里化&#xff08;英语&#xff1a;Currying&#xff09;&#xff0c;又译为卡瑞化或加里化&#xff0c;是把接受多个参数的函数变换成接受一个单一参数&#xff08;最初函数…...

带3090显卡的Linux服务器上部署SDWebui

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

37、Linux中Xsync数据同步备份工具

37、Linux中Xsync数据同步备份工具 一、介绍二、配置集群hostname三、修改xsync文件四、赋权五、安装Rsync六、验证一七、配置免密登录1、生成rsa密钥2、copy机器自身公钥到目标机器3、.ssh/文件目录赋权 八、验证二 ⚠️ 注&#xff1a;本文全程在普通用户下操作&#xff0c;…...

网络基础:构建你的数字世界之桥

I. 引言 A. 什么是网络&#xff1f; 网络是由各种电子设备通过通信线路或无线电波连接起来&#xff0c;以便彼此之间进行数据交换和资源共享的系统。在当今数字化时代&#xff0c;网络已经成为我们生活和工作中不可或缺的一部分&#xff0c;无论是互联网、局域网还是无线通信…...

Python 全栈系列236 rabbit_agent搭建

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

管理自由,体验简单,使用安全 | 详解威联通全套多用户多权限管理方案【附TS-466C产品介绍】

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

【Redis】优惠券秒杀

全局唯一ID 全局唯一ID生成策略&#xff1a; UUIDRedis自增snowflake算法数据库自增 Redis自增ID策略&#xff1a;每天一个key&#xff0c;方便统计订单量ID构造是 时间戳 计数器 Component public class RedisIdWorker {// 2024的第一时刻private static final long BEGIN…...

【几何】平面方程

文章目录 平面方程一般式截距式点法式法线式 平面方程 平面方程是用一个方程来表示平面&#xff0c;平面上的所有点代入方程&#xff0c;方程都成立。因为用法的不同&#xff0c;平面方程一般有四种表现形式。 一般式 设 n ⃗ ( A , B , C ) \vec n(A,B,C) n (A,B,C) 为平…...

macOS访问samba文件夹的正确姿势,在哪里更改“macOS的连接身份“?还真不好找!

环境&#xff1a;路由器上需要身份认证的Mini NAS macOS Sonoma 14 这是一个非常简单的问题&#xff0c;但解决方法却藏得比较深&#xff0c;不够直观&#xff0c;GPT也没有给出明确的解决提示&#xff0c;特意记录一下。 macOS很多地方都很自动&#xff0c;有时候让人找不到设…...

linux进程切换

内核堆栈&#xff1a;每个进程在内核模式下运行时都有自己的内核堆栈。这个堆栈保存了进程在内核模式下的运行状态&#xff0c;包括函数调用时传递的参数、局部变量和返回地址等。 用户态与内核态&#xff1a;进程通常在用户态下运行&#xff0c;当执行系统调用或响应中断时进…...

spring boot 如何升级 Tomcat 版本

在Spring Boot应用程序中升级内嵌的Tomcat版本通常涉及以下几个步骤&#xff1a; 1. 确定当前使用的Tomcat版本 首先&#xff0c;你需要确定你的Spring Boot应用程序当前使用的Tomcat版本。这可以通过查看项目的pom.xml或build.gradle文件来完成&#xff0c;其中会列出所有的…...

sentinel中StatisticSlot数据采集的原理

StatisticSlot数据采集的原理 时间窗口 固定窗口 在固定的时间窗口内&#xff0c;可以允许固定数量的请求进入&#xff1b;超过数量就拒绝或者排队&#xff0c;等下一个时间段进入, 如下图 时间窗长度划分为1秒 单个时间窗的请求阈值为3 上述存在一个问题, 假如9:18:04:…...

图像去噪与增强技术

图像去噪与增强技术是数字图像处理领域中的两个重要方面&#xff0c;它们分别关注消除图像中的噪声和改善图像的质量。 图像去噪技术的主要目的是从受噪声干扰的图像中去除不必要的随机信号&#xff0c;以恢复图像的真实内容。这对于图像的进一步分析和理解至关重要。去噪技术包…...

SpringJPA 做分页条件查询

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

[Java基础揉碎]单例模式

目录 什么是设计模式 什么是单例模式 饿汉式与懒汉式 饿汉式vs懒汉式 懒汉式存在线程安全问题 什么是设计模式 1.静态方法和属性的经典使用 2.设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模式就像是经典的棋谱&am…...

别再死记硬背了!用5个生活化比喻彻底搞懂Linux进程的fork、exec和wait

别再死记硬背了&#xff01;用5个生活化比喻彻底搞懂Linux进程的fork、exec和wait想象你正在厨房准备一顿大餐。菜谱上写着"切菜"、"炒菜"、"装盘"等步骤&#xff0c;但突然发现需要同时处理多道菜品——这时候&#xff0c;你会本能地让家人分工…...

Unity UI交互进阶:手把手教你打造一个支持单击、双击、长按的万能按钮组件

Unity UI交互进阶&#xff1a;手把手教你打造一个支持单击、双击、长按的万能按钮组件在游戏开发中&#xff0c;UI交互的流畅性和多样性直接影响玩家的游戏体验。想象一下&#xff0c;当你在开发一个RPG游戏的背包系统时&#xff0c;需要实现道具的单击查看详情、双击快速使用、…...

通过Taotoken实现Hermes Agent自定义模型供应商接入

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 通过Taotoken实现Hermes Agent自定义模型供应商接入 Hermes Agent是一个流行的AI智能体开发框架&#xff0c;它支持通过配置自定义…...

CentOS 8.5最小化安装后,这5个必做的安全与效率优化设置(附一键脚本)

CentOS 8.5最小化安装后的5个必做安全与效率优化刚完成CentOS 8.5最小化安装的系统就像一张白纸——干净但缺乏生产力。作为运维老手&#xff0c;我见过太多人跳过基础优化直接部署应用&#xff0c;结果在后续使用中频繁遇到权限混乱、软件安装慢、SSH爆破等问题。本文将分享我…...

国内大学生常用的AI写作辅助平台有哪些?

国内高校学生常用的 AI 写作辅助平台&#xff0c;以本土化全流程工具为主&#xff0c;结合通用大模型与专项功能模块&#xff0c;覆盖选题构思、大纲搭建、初稿撰写、语言润色、降重处理、查重检测及格式排版等关键环节&#xff0c;以下是主流平台详解与对比&#xff1a; 一、本…...

保姆级教程:手把手教你搞定ESXi 6.7安装前的BIOS设置(VT-x/VT-d/AES全开)

从零开始&#xff1a;ESXi 6.7安装前的BIOS设置终极指南当你第一次接触企业级虚拟化平台时&#xff0c;那种既兴奋又忐忑的心情我完全理解。作为过来人&#xff0c;我记得自己第一次在Dell PowerEdge服务器上安装ESXi时&#xff0c;光是搞清楚BIOS里那些晦涩的选项就花了整整一…...

从复平面几何到Python代码:可视化理解NumPy中angle()函数的计算原理(附绘图代码)

从复平面几何到Python代码&#xff1a;可视化理解NumPy中angle()函数的计算原理&#xff08;附绘图代码&#xff09; 在数学和工程领域&#xff0c;复数不仅是抽象的概念&#xff0c;更是解决实际问题的有力工具。当我们谈论复数68j时&#xff0c;它不仅仅是一个符号组合——在…...

LLM驱动的高性能计算日志解析技术实践

1. 项目概述&#xff1a;LLM驱动的HPC日志解析革命高性能计算(HPC)系统如同数字世界的巨型望远镜&#xff0c;每天产生PB级的观测数据——系统日志。这些日志记录了从硬件底层到应用层的所有活动&#xff0c;但它们的价值长期被埋没在非结构化文本的泥沼中。传统日志解析方法就…...

番茄小说下载器:永久保存心爱小说的5步终极指南

番茄小说下载器&#xff1a;永久保存心爱小说的5步终极指南 【免费下载链接】fanqienovel-downloader 下载番茄小说 项目地址: https://gitcode.com/gh_mirrors/fa/fanqienovel-downloader 还在为喜爱的番茄小说突然下架而烦恼吗&#xff1f;这款免费开源的番茄小说下载…...

如何用YOLOv5实现FPS游戏智能瞄准:完整实战指南

如何用YOLOv5实现FPS游戏智能瞄准&#xff1a;完整实战指南 【免费下载链接】FPSAutomaticAiming 基于yolov5的FPS游戏AI。 项目地址: https://gitcode.com/gh_mirrors/fp/FPSAutomaticAiming 在竞技射击游戏中&#xff0c;精准瞄准是决定胜负的关键因素&#xff0c;而F…...