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

SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载

文章目录

  • 前言
  • 一、控制器层代码
  • 二、服务层代码
  • 三、代码亮点分析


前言

SpringBoot的同步excel导出方式中,服务会阻塞直到Excel文件生成完毕,如果导出数据很多时,效率低体验差。有效的方案是将导出数据拆分后利用CompletableFuture,将导出任务异步化,并行使用easyExcel导出多个excel文件,最后将所有文件压缩成ZIP格式以方便下载。

Springboot环境下基于以上方案,下面代码的高质量的完成导出销售订单信息到Excel文件,并将多个Excel文件打包成一个ZIP文件,最后发送给客户端:


一、控制器层代码

@RestController
public class SalesOrderController {@Resourceprivate SalesOrderExportService salesOrderExportService;@PostMapping(value = "/salesOrder/export")public void salesOrderExport(@RequestBody @Validated RequestDto req, HttpServletResponse response) {salesOrderExportService.salesOrderExport(req, response);}}

二、服务层代码

负责执行销售订单的导出逻辑:

  1. 将多个Excel文件打包成ZIP文件
  2. 多线程ThreadPoolTaskExecutor并行处理销售订单的导出
@Slf4j
@Service
public class SalesOrderExportService {@Autowired@Qualifier("threadPoolTask")private ThreadPoolTaskExecutor threadPoolTaskExecutor;@Resourceprivate OrderManager OrderManager;public void salesOrderExport(RequestDto req, HttpServletResponse response) {// 获取导出数据,每个SalesOrder实例需要分别导出到一个excel文件List<SalesOrder> orderDataList = OrderManager.getOrder(req.getUserCode());// 略...校验数据InputStream zipFileInputStream = null;Path tempZipFilePath = null;Path tempDir = null;// 获取导出模板try (InputStream templateInputStream = this.getClass().getClassLoader().getResourceAsStream("template/order_template.xlsx");ByteArrayOutputStream outputStream = new ByteArrayOutputStream();) {if (Objects.isNull(templateInputStream)) {throw new RuntimeException("获取模版文件异常");}// 多线程服用一个文件流IOUtils.copy(templateInputStream, outputStream);// 创建临时excel文件导出目录,用于将多个excel导出到此目录下Path tmpDirRef = (tempDir = Files.createTempDirectory(req.userCode() + "dir_prefix"));// 每5个salesOrder一个线程并行导出到excel文件中CompletableFuture[] salesOrderCf = Lists.partition(orderDataList, 5).stream().map(orderDataSubList -> CompletableFuture.supplyAsync(() -> orderDataSubList.stream().map(orderData -> this.exportExcelToFile(tmpDirRef, outputStream, orderData)).collect(Collectors.toList()), threadPoolTaskExecutor).exceptionally(e -> {throw new RuntimeException(e);})).toArray(CompletableFuture[]::new);// 等待所有excel文件导出完成CompletableFuture.allOf(salesOrderCf).get(3, TimeUnit.MINUTES);// 创建临时zip文件tempZipFilePath = Files.createTempFile(req.userCode() + TMP_ZIP_DIR_PRE, ".zip");// 将excel目录下的所有文件压缩到zip文件中,zipUtil有很多工具包都有ZipUtil.zip(tempDir.toString(), tempZipFilePath.toString());response.setContentType("application/octet-stream;charset=UTF-8");response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(tempZipFilePath.toFile().getName(), "utf-8"));// 写zip文件流到responsezipFileInputStream = Files.newInputStream(tempZipFilePath);IOUtils.copy(zipFileInputStream, response.getOutputStream());} catch (Exception e) {log.error("salesOrderExport,异常:", e);throw new RuntimeException("导出异常,请稍后重拾");} finally {try {  // 关闭流if (Objects.nonNull(zipFileInputStream)) {zipFileInputStream.close();}    // 删除临时文件及目录if (Objects.nonNull(tempDir)) {Files.walkFileTree(tempDir, new SimpleFileVisitor<Path>() {@Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {Files.deleteIfExists(file);return FileVisitResult.CONTINUE;}@Overridepublic FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {Files.deleteIfExists(dir);return FileVisitResult.CONTINUE;}});}if (Objects.nonNull(tempZipFilePath)) {Files.deleteIfExists(tempZipFilePath);}} catch (Exception e) {log.error("salesOrderExport, 关闭文件流失败:", e);}} 
  1. 使用EasyExcel库基于模板导出每个销售订单到单独的Excel文件中
    模板内容:
    在这里插入图片描述
/*** 导出单个excle文件,上面的多线程代码调用**/private Path exportExcelToFile(Path temporaryDir, ByteArrayOutputStream templateOutputStream, SalesOrder data) {Path temproaryFilePath = null;try {// 创建临时文件 temproaryFilePath = Files.createTempFile(temporaryDir, data.getOrderNo(), ExcelTypeEnum.XLSX.getValue());} catch (IOException e) {throw new RuntimeException("exportExcelToFile,创建excel临时文件失败:" + data.getOrderNo());}try (InputStream templateInputStream = new ByteArrayInputStream(templateOutputStream.toByteArray());OutputStream temporaryFileOs = Files.newOutputStream(temproaryFilePath);BufferedOutputStream tempOutStream = new BufferedOutputStream(temporaryFileOs)) {// 使用easyExcel的模板功能导出订单数据到临时文件中 ExcelWriter excelWriter = EasyExcel.write(tempOutStream, SalesOrder.class).withTemplate(templateInputStream).excelType(ExcelTypeEnum.XLSX).build();// 填充模板数据WriteSheet writeSheet = EasyExcel.writerSheet().build();FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();excelWriter.fill(new FillWrapper("goods", data.getGoodsList()), fillConfig, writeSheet);excelWriter.fill(data, writeSheet);excelWriter.finish();// temproaryFilePath.toFile().deleteOnExit();return temproaryFilePath;} catch (Exception e) {throw new RuntimeException("exportExcelToFile,导出excel文件失败:" + data.getOrderNo(), e);}}

导出文件如下:
在这里插入图片描述

三、代码亮点分析

多线程处理:

  1. 通过CompletableFutureThreadPoolTaskExecutor,将销售订单的导出任务分配给多个线程并行执行,显著提高了处理大量订单时的性能。
  2. 使用Lists.partition方法将订单列表分割成多个子列表,每个子列表由一个线程处理,这里每5个订单一个线程。

Excel模板导出:

  1. 利用EasyExcel的模板功能,可以基于预定义的Excel模板填充数据,从而生成格式统一的销售订单Excel文件。
  2. 模板文件通过类加载器的getResourceAsStream方法加载,便维护。
  3. 将多个Excel文件打包成一个ZIP文件,方便用户下载和管理。

资源清理:

  1. 方法执行完毕后,及时关闭打开的文件流和删除临时生成的Excel文件和目录,避免了资源泄露。
  2. 使用try-with-resourcestry-catch-finally来确保资源的正确关闭和清理。

错误处理:

  1. 在方法执行过程中,对可能出现的异常进行了捕获和处理,确保服务的健壮。
  2. 对于无法恢复的错误,通过抛出运行时异常的方式通知调用者,并记录了详细的错误日志。

相关文章:

SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载

文章目录 前言一、控制器层代码二、服务层代码三、代码亮点分析 前言 SpringBoot的同步excel导出方式中&#xff0c;服务会阻塞直到Excel文件生成完毕&#xff0c;如果导出数据很多时&#xff0c;效率低体验差。有效的方案是将导出数据拆分后利用CompletableFuture&#xff0c;…...

黑马软件测试第一篇_数据库

说明: 数据库是专门用来存储数据的软件 注意: 对于测试工作而言, 如果项目页面没有实现, 但是我们又想要校验数据,则可以直接通过查询数据库实现 关系: 具体存在的商品录入后 -> 产生对应的数据(存到数据库中) -> 最后会被加载到项目页面中 数据库的分类 分类: 1> 关…...

第十六届蓝桥杯嵌入式组准备

最近我看很多人都在准备蓝桥杯的比赛了&#xff0c;这里我给大家整理一下历届真题或模拟题的讲解与源码 蓝桥杯嵌入式第十二届省赛真题二 蓝桥杯嵌入式第十三届省赛真题一 蓝桥杯嵌入式第十三届省赛真题二 蓝桥杯嵌入式第十四届省赛真题 蓝桥杯嵌入式第十四届模拟考试一 蓝…...

城乡供水信息化系统如何建设?

城乡供水信息化建设是一个综合性的过程&#xff0c;旨在通过现代信息技术提升农村供水系统的管理效率和服务质量。这一过程包含以下关键内容&#xff1a; 一、信息化基础设施建设 感知层建设&#xff1a;在农村饮水工程的关键部位&#xff0c;如水源地、水厂、供水管网等&#…...

【Petri网导论学习笔记】Petri网导论入门学习(七) —— 1.5 并发与冲突

导航 1.5 并发与冲突1.5.1 并发定义 1.14定义 1.15 1.5.2 冲突定义 1.17 1.5.3 一般Petri网系统中的并发与冲突定义 1.18一般网系统中无冲撞概念阻塞&#xff08;有容量函数K的P/T系统&#xff0c;类似于冲撞&#xff09;一般Petri网中并发与冲突共存情况 1.5 并发与冲突 Petr…...

MongoDB常用语句

1.只统计记录总数&#xff1a; let result await CorrectionRecordModel.countDocuments(db);2.数组遍历&#xff0c;循环体中可以有调用异步函数&#xff1a; for(let item of result2){if(item && Tool.checkNotEmptString(item.auth_id) && (item.status …...

自动创作PPT 利用提示词和大模型自动创建ppt

背景 ppt创作可以分为3个步骤&#xff1a;1.大纲撰写&#xff1b;2.内容填充&#xff1b;3.ppt实现。我前几天用十分钟的时间做了一个ppt&#xff0c;主讲大模型测评。这里给大家分享一下我的创作过程。 关于步骤1和步骤2&#xff0c;最近发现一个非常好的提示词&#xff0c;…...

二分类评价指标AUROC和AUPR

文章目录 一、AUROC&#xff08;Area Under the Receiver Operating Characteristic Curve&#xff09;二、AUPR&#xff08;Area Under the Precision-Recall Curve&#xff09;三、区别3.1 案例3.2 如何选择&#xff1f; 在分类任务中&#xff0c; AUROC&#xff08;受试者工…...

雅迪控股营收、净利润和毛利下滑:销量大幅减少,屡屡抽查不合格

《港湾商业观察》廖紫雯 日前&#xff0c;雅迪集团控股有限公司&#xff08;以下简称&#xff1a;雅迪控股&#xff0c;01585.HK&#xff09;发布业绩报告&#xff0c;披露2024年上半年营收净利双下滑等情况&#xff0c;在业绩承压的情况下&#xff0c;雅迪控股遭多家券商下调…...

【网络安全】记一次漏洞挖掘

Spring Cloud Data Flow 热点漏洞详细分析 环境搭建 2.10.0 - 2.11.2版本都可以&#xff0c;这里下的2.11.2 源码下载https://github.com/spring-cloud/spring-cloud-dataflow/tree/v2.11.2 在src/docker-compose里面是有docker文件的&#xff0c;使用docker即可 最近是爆出…...

Redis遇到Hash冲突怎么办?

这是小伙伴之前遇到的一个面试题&#xff0c;感觉也是一个经典八股&#xff0c;和大伙分享下。 一 什么是 Hash 冲突 Hash 冲突&#xff0c;也称为 Hash 碰撞&#xff0c;是指不同的关键字通过 Hash 函数计算得到了相同的 Hash 地址。 Hash 冲突在 Hash 表中是不可避免的&am…...

React综合指南(四)

61、描述React事件处理。 为了解决跨浏览器兼容性问题&#xff0c;React中的事件处理程序将传递SyntheticEvent实例&#xff0c;该实例是React跨浏览器本机事件的跨浏览器包装器。这些综合事件具有与您惯用的本机事件相同的界面&#xff0c;除了它们在所有浏览器中的工作方式相…...

Spring集成Redisson及存取几种基本类型数据

目录 一.什么是Redisson 二.为什么要使用Redisson 三.Spring集成Redisson 1.添加依赖 2.添加配置信息 3.添加redisson配置类 四.Redisson存取各种类型数据 1.字符串(String类型) 存储 获取 2.object对象类型 1.实体类信息 2.存储 3.获取 3.List集合类型 第一种…...

Maplibre-gl\Mapbox-gl改造支持对矢量瓦片加密

Maplibre-gl是Mapbox-gl剔除自带地图服务之后的一个分支,代码很相似。Maplibre-gl\Mapbox-gl使用的pbf格式的矢量瓦片,数据量小,渲染效果好。但也存在着信息泄露的风险。但如果想使用这个开发框架的前端渲染效果,还必须要使用这个格式。最近研究了一下如何对矢量瓦片进行加…...

【功能安全】技术安全概念TSC

目录 01 TSC定义 02 TSC注意事项 03 TSC案例 📖 推荐阅读 01 TSC定义 所处位置 TSC:Technical safety concept技术安全概念 TSR:Technical safety requirement技术安全需求 在系统开发阶段属于安全活动4-6 系统层产品开发示例 TSC目的...

Spark数据源的读取与写入、自定义函数

1. 数据源的读取与写入 1.1 数据读取 读文件 read.jsonread.csv csv文件由两个部分组成&#xff1a;头部数据&#xff08;也就是字段数据&#xff09;、行数据。 read.orc 读数据库 read.jdbc(jdbc连接地址,table‘表名’,properties{‘user’用户名,‘password’密码,‘driv…...

LeetCode 每日一题 2024/10/14-2024/10/20

记录了初步解题思路 以及本地实现代码&#xff1b;并不一定为最优 也希望大家能一起探讨 一起进步 目录 10/14 887. 鸡蛋掉落10/15 3200. 三角形的最大高度10/16 3194. 最小元素和最大元素的最小平均值10/17 3193. 统计逆序对的数目10/18 3191. 使二进制数组全部等于 1 的最少操…...

接口测试(六)jmeter——参数化(配置元件 --> 用户定义的变量)

一、jmeter——参数化&#xff08;配置元件 --> 用户定义的变量&#xff09; 注&#xff1a;示例仅供参考 1. 参数化格式&#xff1a;${变量名} 2. 配置元件&#xff1a;用户定义的变量 3. 添加【用户定义的变量】&#xff0c;【线程组】–>【添加】–>【配置元件】–…...

【学习笔记】网络流

背景 马上ICPC了&#xff0c;很惊奇的发现自己没整理网络流的板子。 最大流 dinic 这里选用的是二分图最大匹配的板子&#xff1a;飞行员配对方案问题 #include<bits/stdc.h> #define int long long using namespace std; const int N1e67,inf1e18; struct E {int to…...

【鸡翅Club】项目启动

一、项目背景 这是一个 C端的社区项目&#xff0c;有博客、交流&#xff0c;面试学习&#xff0c;练题等模块。 项目的背景主要是我们想要通过面试题的分类&#xff0c;难度&#xff0c;打标&#xff0c;来评估员工的技术能力。同时在我们公司招聘季的时候&#xff0c;极大的…...

5秒无损转换B站缓存视频:m4s-converter完整使用指南

5秒无损转换B站缓存视频&#xff1a;m4s-converter完整使用指南 【免费下载链接】m4s-converter 一个跨平台小工具&#xff0c;将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经在B站缓存了珍贵的学习…...

3步解锁鸣潮120帧:你的终极游戏体验优化指南

3步解锁鸣潮120帧&#xff1a;你的终极游戏体验优化指南 【免费下载链接】WaveTools &#x1f9f0;鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools 还在为《鸣潮》游戏中的60帧限制而烦恼吗&#xff1f;明明拥有强大的硬件配置&#xff0c;却无法充…...

AI智能体生态的包管理器:agenticmarket-cli 设计与实践

1. 项目概述&#xff1a;一个面向AI智能体生态的命令行工具如果你和我一样&#xff0c;长期在AI智能体&#xff08;Agent&#xff09;这个领域里折腾&#xff0c;那你肯定经历过这样的场景&#xff1a;为了测试一个最新的开源智能体框架&#xff0c;你需要先找到它的GitHub仓库…...

AI Agent执行链路的安全机制:权限控制与沙箱隔离方案

AI Agent执行链路安全深度解析:权限控制与沙箱隔离全栈落地方案 摘要/引言 你有没有遇到过这些场景:刚上线的企业内部运维Agent被恶意Prompt注入后,直接调用了删除生产库的工具;你做的数据分析Agent被诱导执行了恶意Python代码,把公司的用户隐私数据传到了境外黑客服务器…...

通用框架操作系统:统一异构应用框架的运行时与治理平台

1. 项目概述&#xff1a;一个面向未来的通用框架操作系统最近在开源社区里&#xff0c;一个名为TELLEBO/universal-framework-os的项目引起了我的注意。乍一看这个标题&#xff0c;可能会觉得有点“大词”堆砌的感觉——“通用”、“框架”、“操作系统”&#xff0c;每一个词单…...

从0到1:手把手教你搭建VSCode(附避坑指南,拒绝报错),全程复制粘贴即可

&#x1f525;个人主页&#xff1a;北极的代码&#xff08;欢迎来访&#xff09; &#x1f3ac;作者简介&#xff1a;java后端学习者 ❄️个人专栏&#xff1a;苍穹外卖日记&#xff0c;SSM框架深入&#xff0c;JavaWeb ✨命运的结局尽可永在&#xff0c;不屈的挑战却不可须臾或…...

Cursor IDE事件日志分析工具:Python实现开发者行为可视化与效率洞察

1. 项目概述&#xff1a;一个为开发者“把脉”的智能分析工具如果你是一名开发者&#xff0c;尤其是深度使用Cursor这类AI编程助手的开发者&#xff0c;你肯定有过这样的体验&#xff1a;面对一个复杂的项目&#xff0c;你向AI助手提了无数个问题&#xff0c;生成了大量代码片段…...

Rulebook-AI:用规则引擎为AI智能体构建可控决策框架

1. 项目概述&#xff1a;一个基于规则的AI智能体框架最近在探索如何让AI智能体&#xff08;Agent&#xff09;的行为更可控、更符合业务逻辑时&#xff0c;我遇到了一个挺有意思的开源项目&#xff1a;botingw/rulebook-ai。乍一看这个名字&#xff0c;可能会觉得它又是一个试图…...

OpenAgentsControl:构建多智能体协同系统的开源框架解析

1. 项目概述&#xff1a;一个面向智能体控制的开放框架最近在折腾AI智能体&#xff08;Agent&#xff09;相关的项目&#xff0c;发现一个挺有意思的开源仓库&#xff1a;darrenhinde/OpenAgentsControl。这个项目名字直译过来就是“开放智能体控制”&#xff0c;听起来就很有搞…...

AI智能体GUI交互实战:从原理到实现,让AI玩转桌面应用

1. 项目概述&#xff1a;一个能“玩”游戏的AI智能体最近在AI智能体&#xff08;Agent&#xff09;的圈子里&#xff0c;一个名为“ChattyPlay-Agent”的开源项目引起了我的注意。乍一看名字&#xff0c;你可能会觉得它又是一个基于大语言模型&#xff08;LLM&#xff09;的聊天…...