Java 解析前端上傳 ZIP 壓縮包內 Excel 文件的完整實現方案

使用zip壓縮包上傳excel文件的優點

 	1、體積更小,節約帶寬2、比excel直接讀取更方便攜帶參數及修改3、可以一次性批量導入

Java代碼

Controller

	@PostMapping("/importData")@ApiOperationSupport(order = 3)@ApiOperation(value = "上傳")public R importData(@RequestParam MultipartFile file,@RequestParam("versionId") String versionId) {try {dataTableFjService.importDataSheets(file,versionId);} catch (Exception e) {log.error("上傳文件接口", e);return R.fail("上傳文件接口異常");}return R.success("上傳成功");}

Service

	/*** 導入數據* @param file 壓縮包文件* @param versionId 參數版本id* @throws IOException*/void importDataSheets(MultipartFile file, String dataManagementId, String versionId);

Impl

@Transactional(rollbackFor = Exception.class)
public void importDataSheets(MultipartFile file, String versionId) throws IOException {// 檢查上傳文件是否為空if (file.isEmpty()) {throw new ServiceException("上傳文件為空");}// 檢查版本信息是否存在DataVersionFj dataVersion = dataVersionFjService.getById(versionId);if (Func.isEmpty(dataVersion)) {throw new ServiceException("未查詢到版本信息");}// 創建ZIP輸入流,使用GBK編碼處理中文文件名@Cleanup ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream(), Charset.forName("GBK"));ZipEntry entry = zipInputStream.getNextEntry();// 遍歷ZIP中的所有條目while (entry != null) {if (!entry.isDirectory()) {// 獲取文件名(處理可能包含的路徑分隔符)String entryName = entry.getName();if (entryName.contains("/")) {entryName = entryName.split("/")[1];}// 驗證是否為Excel文件if (!isExcelFile(entryName)) {throw new ServiceException("文件類型有誤");}// 將當前ZIP條目內容讀入字節數組byte[] fileBytes = IOUtils.toByteArray(zipInputStream);// 創建獨立的輸入流,避免關閉ZIP流@Cleanup InputStream is = new ByteArrayInputStream(fileBytes);// 處理Excel文件applicationContext.getBean(XXXImpl.class).processExcelFile(dataVersion, entryName, is);}// 關閉當前ZIP條目,移動到下一個zipInputStream.closeEntry();entry = zipInputStream.getNextEntry();}// 更新數據……
}/*** 處理Excel文件并導入數據到數據庫* * @param dataVersion 數據版本信息* @param fileName Excel文件名* @param zipInputStream Excel文件輸入流* @throws ServiceException 當文件處理失敗時拋出異常*/
@Transactional(rollbackFor = Exception.class)
public void processExcelFile(DataVersionFj dataVersion, String fileName, InputStream zipInputStream) {Workbook workbook = null;try {// 根據文件擴展名創建不同格式的Workbookworkbook = fileName.endsWith("xlsx") ? new XSSFWorkbook(zipInputStream) :new HSSFWorkbook(zipInputStream);} catch (Exception e) {e.printStackTrace();throw new ServiceException("[" + fileName + "],文件打開失敗,請核對文件內容");}// 解析表名并獲取對應的枚舉類型String[] tableNames = fileName.split("[.]");TableNameEnum tableNameEnum = TableNameEnum.getByName(tableNames[0]);// 存儲Excel前三行表頭信息(用于處理多級表頭)List<String> fieldMeaningNames = null;  // 第一行表頭List<String> fieldMeaningNames2 = null; // 第二行表頭List<String> fieldMeaningNames3 = null; // 第三行表頭// 獲取第一個工作表Sheet dataSheet = workbook.getSheetAt(0);int dataRow = 0; // 數據起始行索引// 讀取前3行表頭信息Iterator<Row> rowIterator = dataSheet.iterator();int dataIndex = 0;while (rowIterator.hasNext() && dataIndex < 3) {Row headRow = rowIterator.next();Iterator<Cell> cellIterator = headRow.cellIterator();// 獲取當前行的有效表頭內容List<String> fieldMeaningNamesThis = getFieldMeaningName(cellIterator);// 處理有效行(跳過空行)if (Func.isNotEmpty(fieldMeaningNamesThis)) {if (isEmptyRow(fieldMeaningNamesThis, fileName)) {continue; // 跳過無效行}// 按行索引分配表頭信息switch (dataIndex) {case 0:fieldMeaningNames = fieldMeaningNamesThis;break;case 1:fieldMeaningNames2 = fieldMeaningNamesThis;break;case 2:fieldMeaningNames3 = fieldMeaningNamesThis;break;}dataRow = dataIndex;}dataIndex++;}// 驗證表頭是否存在if (fieldMeaningNames == null || fieldMeaningNames.size() == 0) {throw new ServiceException("上傳文件內容為空");}try {// 處理多級表頭的合并單元格情況(向下繼承父級表頭)if (Func.isNotEmpty(fieldMeaningNames3)) {for (int i = 1; i < fieldMeaningNames3.size(); i++) {if (Func.isNotBlank(fieldMeaningNames3.get(i)) && Func.isBlank(fieldMeaningNames2.get(i))) {// 向上查找最近的非空父級表頭for (int j = i - 1; j >= 0; j--) {if (Func.isNotBlank(fieldMeaningNames2.get(j))) {fieldMeaningNames2.set(i, fieldMeaningNames2.get(j));break;}}}}}// 處理二級表頭的合并單元格情況if (Func.isNotEmpty(fieldMeaningNames2)) {for (int i = 1; i < fieldMeaningNames2.size(); i++) {if (Func.isNotBlank(fieldMeaningNames2.get(i)) && Func.isBlank(fieldMeaningNames.get(i))) {// 向上查找最近的非空父級表頭for (int j = i - 1; j >= 0; j--) {if (Func.isNotBlank(fieldMeaningNames.get(j))) {fieldMeaningNames.set(i, fieldMeaningNames.get(j));break;}}}}}// 構建列名和位置信息List<Map<String, Object>> fieldMeaningModify = new ArrayList<>();List<Map<String, Object>> fieldMeaningTemp = new ArrayList<>();for (int i = 0; i < fieldMeaningNames.size(); i++) {Map<String, Object> item = new HashMap<>();item.put("name", fieldMeaningNames.get(i));item.put("index", i);fieldMeaningTemp.add(item);}// 如果沒有自定義列映射,使用臨時列信息if (fieldMeaningModify.size() == 0) {fieldMeaningModify = fieldMeaningTemp;}//自行處理excel數據列表內容……} catch (Exception e) {log.error("插入數據異常:{}", e.getMessage());e.printStackTrace();throw new ServiceException("導入異常");}
}
/*** 獲取Excel行中所有單元格的值* * @param cellIterator 單元格迭代器* @return 包含所有單元格格式化后值的列表*/
public List<String> getFieldMeaningName(Iterator<Cell> cellIterator) {// 存儲當前行所有單元格的值List<String> fieldMeaningNames = new ArrayList<>();// 創建數據格式化器,用于正確處理不同類型的單元格值DataFormatter dataFormatter = new DataFormatter();// 遍歷當前行的所有單元格while (cellIterator.hasNext()) {Cell cell = cellIterator.next();// 使用DataFormatter獲取單元格的格式化文本值// 例如:數字類型會保留格式(如百分比、貨幣),日期類型會轉為字符串String value = dataFormatter.formatCellValue(cell);// 將單元格值添加到列表中fieldMeaningNames.add(value);}return fieldMeaningNames;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/90357.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/90357.shtml
英文地址,請注明出處:http://en.pswp.cn/web/90357.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【shell腳本編程】day1 備份指定文件類型

文章目錄1、腳本要求2、腳本編寫3、腳本解釋4、腳本改進1、腳本要求 編寫一個腳本&#xff0c;遍歷/data/目錄下的.txt文件將這些txt文件做一個備份備份的文件名增加一個年月日的后綴&#xff0c;比如將aming.txt備份為aming.txt_20231001 2、腳本編寫 [rootlocalhost shell…

Gata 攜手 Walrus 構建 AI 的開放執行基礎設施

致力于開發去中心化大模型推理、訓練和數據技術的 Gata&#xff0c;現已整合 Walrus&#xff0c;作為其 AI 開放執行基礎設施的核心組件。Walrus 將為 Gata 的首款產品 DataAgent 提供關鍵的數據層&#xff0c;助力其全套應用&#xff0c;將去中心化 AI 的優勢直接帶給用戶&…

DNS及DNS域名解析流程

文章目錄什么是DNS域名解析DNS服務器DNS域名解析流程什么是DNS域名解析 我們首先要了解域名和IP地址的區別。IP地址是互聯網上計算機唯一的邏輯地址&#xff0c;通過IP地址實現不同計算機之間的相互通信&#xff0c;每臺聯網計算機都需要通過IP地址來互相聯系和分別。 但由于I…

用 STM32 的 SYSTICK 定時器與端口復用重映射玩轉嵌入式開發

目錄 1. SYSTICK 定時器的基本功:時間管理大師 1.1 SYSTICK 的核心寄存器與工作原理 1.2 配置 SYSTICK 的正確姿勢 1.3 實戰:用 SYSTICK 實現精準延時 1.4 小技巧:SYSTICK 的低功耗優化 2. SYSTICK 中斷:讓你的程序“活”起來 2.1 配置 SYSTICK 中斷 2.2 實戰:用 S…

Sa-Token:輕量級Java權限認證框架使用指南

一、Sa-Token簡介 Sa-Token 是一個專注于權限認證的輕量級 Java 框架&#xff0c;旨在簡化登錄認證、權限控制等功能的實現。其核心功能包括&#xff1a; 登錄認證&#xff1a;通過 Token 機制管理用戶會話&#xff0c;支持單點登錄&#xff08;SSO&#xff09;。權限認證&am…

動態 vs 靜態住宅代理,哪種更適合廣告投手?

在廣告投放行業&#xff0c;無論你是跑Facebook、Google Ads&#xff0c;還是做TikTok、原生廣告&#xff0c;代理IP幾乎是繞不開的話題。而選擇動態住宅代理還是靜態住宅代理&#xff0c;對廣告賬戶的穩定性、投放質量甚至生命周期都有直接影響。本篇文章將結合IPFoxy代理&…

命題是一種清晰、確定的表達。通過學習命題,來幫助你更清晰地表達自己的思想。

文章目錄 引言 I 命題的特點 可以判斷真偽 同一性 II 有效溝通的小技巧 多用陳述句,少用感嘆句和疑問句。 在表述意思時,多用名詞,少用代詞;多用具體的名詞,少用抽象的名詞,避免造成不必要的歧義。 正確找托詞 引言 要進行有效的邏輯推理,第一步是把我們的想法,我們要…

IPV6地址與IPV4有什么區別?

作為互聯網協議的迭代版本&#xff0c;IPV6&#xff08;Internet Protocol Version 6&#xff09;與IPV4&#xff08;Internet Protocol Version 4&#xff09;在設計理念和功能特性上存在顯著差異。本文將簡要解析兩者的核心區別&#xff0c;幫助讀者理解IPV6的優勢與必要性。…

python 什么時候應該用函數式編程,什么時候應該用面向對象?

在 Python 這個多范式語言中&#xff0c;選擇使用函數式編程&#xff08;Functional Programming, FP&#xff09;還是面向對象編程&#xff08;OOP&#xff09;并非一個非黑即白的選擇&#xff0c;而更像是在一個工具箱中為特定的任務挑選最合適的工具。 我們可以用一個比喻來…

【設計模式】迭代器模式 (游標(Cursor)模式)

迭代器模式&#xff08;Iterator Pattern&#xff09;詳解一、迭代器模式簡介 迭代器模式&#xff08;Iterator Pattern&#xff09; 是一種 行為型設計模式&#xff08;對象行為型模式&#xff09;&#xff0c;它提供了一種方法來順序訪問一個聚合對象中的各個元素&#xff0c…

docker安裝 Elasticsearch、Kibana、IK 分詞器

Elasticsearch 1.拉去鏡像 docker pull elasticsearch:8.12.2 docker pull kibana&#xff1a;8.12.22.創建掛載目錄 mkdir /root/elasticsearch3.不掛載啟動 docker run -d \ --restartalways \ --name fusion_elasticsearch \ --network fusion_network \ -p 9200:9200 \ -p …

Java面試寶典:Spring專題二

一、介紹下Spring中的事務 1.Spring事務的本質與價值 Spring事務本質是基于AOP的聲明式事務封裝,通過代理機制在目標方法前后注入事務管理邏輯(開啟、提交/回滾)。其核心價值在于: 業務解耦:將事務控制從業務代碼剝離,通過配置或注解管理(如@Transactional)。 統一抽…

DGMR壓縮技術:讓大規模視覺Transformer模型體積減半而性能不減

Transformer架構展現出卓越的擴展特性&#xff0c;其性能隨模型容量增長而持續提升。大規模模型在獲得優異性能的同時&#xff0c;也帶來了顯著的計算和存儲開銷。深入分析主流Transformer架構發現&#xff0c;多層感知器&#xff08;MLP&#xff09;模塊占據了模型參數的主要部…

JavaWeb學習打卡14(JSP內置對象及作用域)

JSP 中9 大內置對象PageContext // 用來存東西Request // 用來存東西ResponseSession // 用來存東西Application &#xff08;ServletContext&#xff09; // 用來存東西config &#xff08;ServletConfig&#xff09;outpage…

濤思數據參與起草中國工業互聯網研究院《工業數據庫規范》全系列標準

最近&#xff0c;《工業數據庫規范》系列團體標準正式發布。該標準由中國工業互聯網研究院牽頭&#xff0c;中國移動通信聯合會發布&#xff0c;共分為三部分—— 第1部分&#xff1a;云數據庫第2部分&#xff1a;實時數據庫第3部分&#xff1a;時序數據庫 濤思數據作為三項標…

使用exceljs導出luckysheet表格 純前端 支持離線使用

一.技術 exceljs&#xff0c;luckysheet 二.實現 參考網上博文exceljs對導出lucksheet表格的實現,發現存在一些問題并給予修復: 1.字體顏色、字號&#xff0c;加粗等適配的問題. 2.單元格對齊方式不生效; 3.單元格邊框無法繪制; 4.單元格邊框顏色及線型錯亂; 5.單元格列…

從0到1學習c++ 命名空間

也是好久沒寫博客了&#xff0c;主播這半年一直在忙別的領域&#xff0c;在磁力驅動領域干了一年&#xff0c;最好發現自己對這個領域并不是很感興趣&#xff0c;做這個領域多半都是為了發文章&#xff0c;現在閑下來了&#xff0c;主播終于也是過上好日子了&#xff0c;主播又…

大模型提示詞漏洞攻防測試:技術分析與實踐指南

引言 隨著ChatGPT、Claude、Gemini等大型語言模型(LLMs)的廣泛應用&#xff0c;它們已經成為現代AI系統的核心組件&#xff0c;被整合到各種產品和服務中。這些模型通過提示。Prompts)與用戶進行交互&#xff0c;而提示詞作為人類與AI溝通的橋梁&#xff0c;其安全性變得尤為重…

Golang實現 - 實現只有表頭的 Excel 模板,并在指定列添加了下拉框功能。生成的 Excel 文件在打開時,指定列的單元格會顯示下拉選項

該版本完全兼容最新版 excelize 庫 (v2.7)&#xff0c;實現了只有表頭的 Excel 模板&#xff0c;并在指定列添加了下拉框功能。生成的 Excel 文件在打開時&#xff0c;指定列的單元格會顯示下拉選擇箭頭。代碼如下&#xff1a;package mainimport ("fmt""log&qu…

全連接隊列

監聽套接字使用socket接口創建一個套接字&#xff0c;然后bind給套接字綁定地址&#xff0c;最后listen將套接字設置為監聽套接字。監聽套接字以前理解是三元組標識&#xff0c;后面看了netstat&#xff0c;覺得應該是五元組&#xff0c;只不過它這個五元組是{協議&#xff0c;…