Execel文档批量替换标签实现方案

问题背景

需求:俺现网班级作为维度,批量导出每个班级学员的数据,excel的个数在1k左右,每一张表的人数在90左右。导出总耗时在10小时左右。

代码编写完成并导出现网数据后,发现导出的标题错了。

解决方案

1.通过修改代码,重新导出。(耗时在10h)

2.通过java 代码实现excel标签替换。(耗时在10分钟)

代码实现

依赖

    implementation "org.apache.poi:poi:5.2.3"implementation "org.apache.poi:poi-ooxml:5.2.3"

代码 

其中当文件中只有旧的标签且其他数据不存在时,会直接报错,需要我们手动处理即可。

template为我们新模板的样式文件,sourseDir为旧excel的文件夹。outputDir为新文件的生成位置。

根据样式的实际行数修改readTemplateData中的循环行数。removeRows方法中设置旧excel中标签的起止行数。(索引从0开始)

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class ExecelUtios {public static void main(String[] args) {String templatePath = "C:\\Users\\fjhb\\Desktop\\test111\\template.xlsx";String sourceDir = "C:\\Users\\fjhb\\Desktop\\教育学院\\4考勤\\2023";String outputDir = "C:\\Users\\fjhb\\Desktop\\教育学院\\4考勤最总\\2023\\";try {// 读取模板数据(内容和样式)TemplateData templateData = readTemplateData(templatePath);List<String> errorFileName = new ArrayList<>();File dir = new File(sourceDir);File[] files = dir.listFiles((d, name) ->name.toLowerCase().endsWith(".xls") || name.toLowerCase().endsWith(".xlsx"));if (files == null || files.length == 0) {System.out.println("目录中没有Excel文件");return;}System.out.println("开始处理 " + files.length + " 个文件...");for (File file : files) {try {processFile(file, templateData, outputDir);System.out.println("✓ 已处理: " + file.getName());} catch (Exception e) {System.err.println("✗ 处理失败: " + file.getName() + " - " + e.getMessage());errorFileName.add(file.getName());}}System.out.println("处理完成! 成功处理 " + files.length + " 个文件");if(!errorFileName.isEmpty()) {System.out.println("执行错误,需要手动处理的文件为下(因为文件没有内容只有标题):");for (String s : errorFileName) {System.err.println(s);}}} catch (Exception e) {e.printStackTrace();}}// 读取模板数据(包含样式、内容和合并单元格)private static TemplateData readTemplateData(String templatePath) throws IOException {try (InputStream is = new FileInputStream(templatePath);Workbook templateWorkbook = WorkbookFactory.create(is)) {Sheet sheet = templateWorkbook.getSheetAt(0);List<RowData> rows = new ArrayList<>();List<CellRangeAddress> mergedRegions = new ArrayList<>();// 读取前三行for (int i = 0; i < 3 && i <= sheet.getLastRowNum(); i++) {Row row = sheet.getRow(i);if (row != null) {rows.add(new RowData(row, templateWorkbook));}}// 读取前三行的合并单元格区域for (int i = 0; i < sheet.getNumMergedRegions(); i++) {CellRangeAddress mergedRegion = sheet.getMergedRegion(i);if (mergedRegion.getLastRow() < 3) { // 只处理前三行的合并mergedRegions.add(mergedRegion);}}return new TemplateData(rows, mergedRegions, templateWorkbook);}}// 处理单个文件private static void processFile(File file, TemplateData templateData, String outputDir)throws IOException {try (InputStream is = new FileInputStream(file);Workbook workbook = WorkbookFactory.create(is)) {Sheet sheet = workbook.getSheetAt(0);// 1. 删除原有的合并区域(前三行)removeMergedRegionsInRange(sheet, 0, 2);// 2. 删除原有的前两行removeRows(sheet, 0, 1);// 3. 插入模板行(带样式)insertTemplateRows(sheet, templateData, workbook);// 4. 确保输出目录存在File outDir = new File(outputDir);if (!outDir.exists()) outDir.mkdirs();// 5. 保存文件String outputPath = outputDir + File.separator + file.getName();try (OutputStream os = new FileOutputStream(outputPath)) {workbook.write(os);}}}// 删除指定行范围内的合并区域private static void removeMergedRegionsInRange(Sheet sheet, int startRow, int endRow) {for (int i = sheet.getNumMergedRegions() - 1; i >= 0; i--) {CellRangeAddress mergedRegion = sheet.getMergedRegion(i);if (mergedRegion.getFirstRow() >= startRow && mergedRegion.getLastRow() <= endRow) {sheet.removeMergedRegion(i);}}}// 删除指定行范围private static void removeRows(Sheet sheet, int startRow, int endRow) {// 删除行内容for (int i = startRow; i <= endRow; i++) {Row row = sheet.getRow(i);if (row != null) {sheet.removeRow(row);}}// 移动行if (endRow < sheet.getLastRowNum()) {sheet.shiftRows(endRow + 1, sheet.getLastRowNum(), -(endRow - startRow + 1));}}// 插入模板行(带样式)private static void insertTemplateRows(Sheet sheet, TemplateData templateData, Workbook targetWorkbook) {if (templateData.rows.isEmpty()) return;// 移动现有行sheet.shiftRows(0, sheet.getLastRowNum(), templateData.rows.size(), true, true);// 创建新行并应用模板for (int i = 0; i < templateData.rows.size(); i++) {Row newRow = sheet.createRow(i);templateData.rows.get(i).applyTo(newRow, targetWorkbook, templateData.sourceWorkbook);}// 添加合并区域for (CellRangeAddress mergedRegion : templateData.mergedRegions) {sheet.addMergedRegion(mergedRegion);}}// 模板数据容器static class TemplateData {final List<RowData> rows;final List<CellRangeAddress> mergedRegions;final Workbook sourceWorkbook;public TemplateData(List<RowData> rows, List<CellRangeAddress> mergedRegions, Workbook sourceWorkbook) {this.rows = rows;this.mergedRegions = mergedRegions;this.sourceWorkbook = sourceWorkbook;}}// 行数据容器static class RowData {private final List<CellData> cells = new ArrayList<>();public RowData(Row sourceRow, Workbook sourceWorkbook) {if (sourceRow != null) {for (Cell cell : sourceRow) {cells.add(new CellData(cell, sourceWorkbook));}}}public void applyTo(Row targetRow, Workbook targetWorkbook, Workbook sourceWorkbook) {for (CellData cellData : cells) {cellData.applyTo(targetRow, targetWorkbook, sourceWorkbook);}}}// 单元格数据容器(包含样式)static class CellData {private final int columnIndex;private final CellStyle sourceStyle;private final Object value;private final CellType cellType;private final Workbook sourceWorkbook;public CellData(Cell sourceCell, Workbook sourceWorkbook) {this.columnIndex = sourceCell.getColumnIndex();this.sourceStyle = sourceCell.getCellStyle();this.sourceWorkbook = sourceWorkbook;this.cellType = sourceCell.getCellType();switch (cellType) {case STRING:value = sourceCell.getStringCellValue();break;case NUMERIC:value = sourceCell.getNumericCellValue();break;case BOOLEAN:value = sourceCell.getBooleanCellValue();break;case FORMULA:value = sourceCell.getCellFormula();break;case BLANK:value = "";break;default:value = null;}}public void applyTo(Row targetRow, Workbook targetWorkbook, Workbook sourceWorkbook) {Cell newCell = targetRow.createCell(columnIndex);// 复制单元格值setCellValue(newCell, value, cellType);// 复制单元格样式(深度复制)if (sourceStyle != null) {CellStyle newStyle = targetWorkbook.createCellStyle();copyCellStyleDeep(newStyle, sourceStyle, targetWorkbook, sourceWorkbook);newCell.setCellStyle(newStyle);}}private void setCellValue(Cell cell, Object value, CellType cellType) {if (value == null) return;switch (cellType) {case STRING:cell.setCellValue((String) value);break;case NUMERIC:cell.setCellValue((Double) value);break;case BOOLEAN:cell.setCellValue((Boolean) value);break;case FORMULA:cell.setCellFormula((String) value);break;case BLANK:cell.setBlank();break;default:}}// 深度复制单元格样式(支持.xls和.xlsx)private void copyCellStyleDeep(CellStyle newStyle, CellStyle sourceStyle,Workbook targetWorkbook, Workbook sourceWorkbook) {// 复制基本样式属性newStyle.setAlignment(sourceStyle.getAlignment());newStyle.setVerticalAlignment(sourceStyle.getVerticalAlignment());newStyle.setBorderTop(sourceStyle.getBorderTop());newStyle.setBorderBottom(sourceStyle.getBorderBottom());newStyle.setBorderLeft(sourceStyle.getBorderLeft());newStyle.setBorderRight(sourceStyle.getBorderRight());newStyle.setTopBorderColor(sourceStyle.getTopBorderColor());newStyle.setBottomBorderColor(sourceStyle.getBottomBorderColor());newStyle.setLeftBorderColor(sourceStyle.getLeftBorderColor());newStyle.setRightBorderColor(sourceStyle.getRightBorderColor());newStyle.setFillPattern(sourceStyle.getFillPattern());// 复制背景色if (sourceStyle.getFillBackgroundColor() > 0) {newStyle.setFillBackgroundColor(sourceStyle.getFillBackgroundColor());}// 复制前景色if (sourceStyle.getFillForegroundColor() > 0) {newStyle.setFillForegroundColor(sourceStyle.getFillForegroundColor());}// 复制其他属性newStyle.setDataFormat(sourceStyle.getDataFormat());newStyle.setWrapText(sourceStyle.getWrapText());newStyle.setIndention(sourceStyle.getIndention());newStyle.setRotation(sourceStyle.getRotation());newStyle.setHidden(sourceStyle.getHidden());newStyle.setLocked(sourceStyle.getLocked());newStyle.setShrinkToFit(sourceStyle.getShrinkToFit());// 复制字体Font sourceFont = sourceWorkbook.getFontAt(sourceStyle.getFontIndex());Font newFont = targetWorkbook.createFont();copyFontDeep(newFont, sourceFont, targetWorkbook, sourceWorkbook);newStyle.setFont(newFont);}// 深度复制字体样式private void copyFontDeep(Font newFont, Font sourceFont,Workbook targetWorkbook, Workbook sourceWorkbook) {newFont.setBold(sourceFont.getBold());newFont.setColor(sourceFont.getColor());newFont.setFontHeight(sourceFont.getFontHeight());newFont.setFontHeightInPoints(sourceFont.getFontHeightInPoints());newFont.setFontName(sourceFont.getFontName());newFont.setItalic(sourceFont.getItalic());newFont.setStrikeout(sourceFont.getStrikeout());newFont.setTypeOffset(sourceFont.getTypeOffset());newFont.setUnderline(sourceFont.getUnderline());newFont.setCharSet(sourceFont.getCharSet());}}}

批量执行即可。

执行效果为下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90649.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90649.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot配置多数据源多数据库

Springboot支持配置多数据源。默认情况&#xff0c;在yml文件中只会配置一个数据库。如果涉及到操作多个数据库的情况&#xff0c;在同实例中&#xff08;即同一个ip地址下的不同数据库&#xff09;&#xff0c;可以采用数据库名点数据库表的方式&#xff0c;实现跨库表的操作。…

Rocky9.4部署Zabbix7

一、配置安装源 rpm -Uvh https://repo.zabbix.com/zabbix/7.0/rocky/9/x86_64/zabbix-release-7.0-5.el9.noarch.rpm ​ yum clean all 二、安装Zabbix server&#xff0c;Web前端&#xff0c;agent yum install zabbix-server-mysql zabbix-web-mysql zabbix-nginx-conf z…

【Java】对象类型转换(ClassCastException)异常:从底层原理到架构级防御,老司机的实战经验

在开发中&#xff0c;ClassCastException&#xff08;类转换异常&#xff09;就像一颗隐藏的定时炸弹&#xff0c;常常在代码运行到类型转换逻辑时突然爆发。线上排查问题时&#xff0c;这类异常往往因为类型关系复杂而难以定位。多数开发者习惯于在转换前加个instanceof判断就…

探路者:用 AI 面试加速人才集结,为户外爱好者带来更专业的服务

作为深耕户外用品领域的知名品牌&#xff0c;探路者已构建起覆盖全国的销售服务网络&#xff0c;上千品种的产品矩阵更是为品牌在市场中站稳脚跟提供了有力支撑。对探路者来说&#xff0c;要持续为户外爱好者带来专业且贴心的体验&#xff0c;专业人才是核心支撑。然而&#xf…

LeetCode——面试题 05.01 插入

通过万岁&#xff01;&#xff01;&#xff01; 题目&#xff1a;一共会给四个数&#xff0c;分别是N、M、i、j&#xff0c;然后希望我们把N和M抓怒换为2进制以后&#xff0c;将M的二进制放在i到j之间的区域&#xff0c;如果M的二进制长度小于i-j1&#xff0c;则前面补0即可。最…

前端设计中如何在鼠标悬浮时同步修改块内样式

虽然只是一个小问题&#xff0c;但这个解决问题的过程也深化了自己对盒子模型的理解问题缘起正在写一个登录注册的小窗口&#xff0c;想要在鼠标悬浮阶段让按钮和文字都变色&#xff0c;但是发现实操的时候按钮和文字没办法同时变色鼠标悬停前鼠标悬停后问题分析仔细分析了下该…

航空发动机高速旋转件的非接触式信号传输系统

航空发动机是飞机动力系统的核心&#xff0c;各种关键部件如涡轮、压气机等&#xff0c;经常处于极端高温、高速旋转的工作环境中。航空发动机内的传感器数据&#xff0c;如何能够稳定可靠的通过无线的方式传输到检测太&#xff0c;一直是业内的一个难点和痛点。在这个领域&…

【postgresql按照逗号分割字段,并统计数量和求和】

postgresql按照逗号分割字段&#xff0c;并统计数量和求和postgresql按照逗号分割字段&#xff0c;并统计数量和求和postgresql按照逗号分割字段&#xff0c;并统计数量和求和 SELECT ucd, p ,tm, step, unitcd, tm_end from resource_calc_scene_rain_bound_value_plus whe…

「iOS」————继承链与对象的结构

iOS学习前言对象的底层结构isa的类型isa_tobjc_class & objc_object类信息的静态与动态存储&#xff08;ro、rw、rwe机制&#xff09;cachebits继承链isKindOfClass和isMemberOfClassisKindOfClass:isMemberofClass前言 对 对象底层结构的相关信息有点遗忘&#xff0c;简略…

代码随想录day46dp13

647. 回文子串 题目链接 文章讲解 回溯法 class Solution { public:int count 0;// 检查字符串是否是回文bool isPalindrome(string& s, int start, int end) {while (start < end) {if (s[start] ! s[end]) return false;start;end--;}return true;}// 回溯法&#…

学习随笔录

#61 学习随笔录 今日的思考 &#xff1a; 反思一下学习效率低下 不自律 或者 惰性思维 懒得思考 又或者 好高婺远 顶级自律从不靠任何意志力&#xff0c;而在于「平静如水的野心」_哔哩哔哩_bilibili 然后上面是心灵鸡汤合集 vlog #79&#xff5c;程序员远程办公的一天…

python-函数进阶、容器通用方法、字符串比大小(笔记)

python数据容器的通用方法#记住排序后容器类型会变成list容器列表 list[1,3,5,4,6,7] newListsorted(list,reverseTrue) print(newList) [7, 6, 5, 4, 3, 1]list[1,3,5,4,6,7] newListsorted(list,reverseFalse) print(newList) [1, 3, 4, 5, 6, 7]字典排序的是字典的key字符串…

关闭chrome自带的跨域限制,简化本地开发

在开发时为了图方便,简化本地开发,懒得去后端配置允许跨域,那就可以用此方法1. 右键桌面上的Chrome浏览器图标&#xff0c;选择“创建快捷方式”到桌面。2. 在新创建的快捷方式的图标上右键&#xff0c;选择“属性”。3. 在弹出窗口中的“目标”栏中追加&#xff1a; --allow-r…

C++___快速入门(上)

第一个C程序#include<iostream> using namespace std; int main() {cout << "hello world !" << endl;return 0; }上边的代码就是用来打印字符串 “hello world !” 的&#xff0c;可见&#xff0c;与C语言还是有很大的差别的&#xff0c;接下来我…

构建企业级Docker日志驱动:将容器日志无缝发送到腾讯云CLS

源码地址:https://github.com/k8scat/docker-log-driver-tencent-cls 在现代云原生架构中,容器化应用已经成为主流部署方式。随着容器数量的快速增长,如何高效地收集、存储和分析容器日志成为了一个关键挑战。传统的日志收集方式往往存在以下问题: 日志分散在各个容器中,难…

Kafka——消费者组重平衡能避免吗?

引言 其实在消费者组到底是什么&#xff1f;中&#xff0c;我们讲过重平衡&#xff0c;也就是Rebalance&#xff0c;现在先来回顾一下这个概念的原理和用途。它是Kafka实现消费者组&#xff08;Consumer Group&#xff09;弹性伸缩和容错能力的核心机制&#xff0c;却也常常成…

使用爬虫获取游戏的iframe地址

如何通过爬虫获取游戏的iframe地址要获取网页中嵌入的游戏的iframe地址&#xff08;即iframe元素的src属性&#xff09;&#xff0c;您可以使用网络爬虫技术。iframe是HTML元素&#xff0c;用于在当前页面中嵌入另一个文档&#xff08;如游戏页面&#xff09;&#xff0c;其地址…

NTLite Ent Version

NTLite是一款专业的系统安装镜像制作工具&#xff0c;通过这款软件可以帮助用户快速生成镜像文件打好补丁&#xff0c;很多朋友在安装电脑系统的时候一般都安装了windows系统的所有Windows组件&#xff0c;其实有很多Windows组件你可能都用到不到&#xff0c;不如在安装系统时就…

Maven之依赖管理

Maven之依赖管理一、Maven依赖管理的核心价值二、依赖的基本配置&#xff08;坐标与范围&#xff09;2.1 依赖坐标&#xff08;GAV&#xff09;2.2 依赖范围&#xff08;scope&#xff09;示例&#xff1a;常用依赖范围配置三、依赖传递与冲突解决3.1 依赖传递性示例&#xff1…

【Unity实战100例】Unity资源下载系统开发流程详解(移动端、PC端 ,局域网控制台服务)

目录 一、项目概述 二、服务器开发 1、配置文件设计 1、加载配置 2. 处理客户端请求 3. 文件下载处理 三、客户端开发 1、配置管理 1、配置加载与保存 2、下载任务管理 1、任务类设计 2、下载队列管理 3、核心下载流程 四、UI系统实现 五、部署与测试 1、服务…