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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;…