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
英文地址,請注明出處:http://en.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、服務…