java将文件压缩打包后进行下载
今天受到一个需求,需要查出文件,然后将文件打包后下载。看了下项目里默认代码有压缩功能,以此修改了下,项目使用了hutool。项目是若依项目
定义zip的数据传输对象,ossId可以是文件表的id
@Data
public class SysOssZipDTO {/*** 关联OSS对象存储ID*/
})private String ossId;/*** 压缩包文件内文件夹路径,例子(main/java/com/haoyu/flowForm/domain/)*/private String zipItemFolderPath;}
sevice层
void downloadZip(List<SysOssZipDTO> zipDtoList, HttpServletResponse response, String taskId) throws IOException;serviceImpl层
 @Overridepublic void downloadZip(List<SysOssZipDTO> zipDtoList, HttpServletResponse response, String taskId) throws IOException {if(zipDtoList == null || zipDtoList.isEmpty()){return;}ZipOutputStream zip = new ZipOutputStream(response.getOutputStream());final int bufferSize = 1024 * 1024 * 5;      // 5MBfinal byte[] buffer = new byte[bufferSize];long totalFileSize = 0;  // 文件总大小,估计值// 任务进度keyfinal String progressKey = OssConstant.SYS_OSS_PROGRESS+taskId;for (int i =0;i<zipDtoList.size();i++){// 设置进度(可选,用于压缩进度查询,就是将进度存入redis里,然后设计个接口供前端查询进度)/*SysOssProgressBO sysOssProgressBO = new SysOssProgressBO();sysOssProgressBO.setProgressNumber(Long.valueOf(i));sysOssProgressBO.setTotalProgress(Long.valueOf(zipDtoList.size()));RedisUtils.setCacheObject(progressKey,sysOssProgressBO, Duration.ofMinutes(10));*/SysOssZipDTO zipDTO = zipDtoList.get(i);if(StrUtil.isEmpty(zipDTO.getOssId())|| StrUtil.isEmpty(zipDTO.getZipItemFolderPath())){continue;}// 通过id查找文件信息SysOssVo sysOss = matchingUrl(SpringUtils.getAopProxy(this).getById(zipDTO.getOssId()));if (ObjectUtil.isNull(sysOss)) {continue;}// 通过文件信息获取文件流,本文用的Oss存储,如果用别的可以自己写,核心就是读取流OssClient storage = OssFactory.instance(sysOss.getService());try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl());){// 这里使用文件名了,拼上前缀序号解决文件名重复问题zip.putNextEntry(new ZipEntry(zipDTO.getZipItemFolderPath() +"("+i+")"+sysOss.getOriginalName().replaceAll("/","_" )));int available = inputStream.available();totalFileSize += available;// 防Oom,可以用下面注释的也可以用hutool的/*int len = 0;while((len = inputStream.read(buffer)) != -1){zip.write(buffer, 0, len);}*/IoUtil.copy(inputStream, zip, bufferSize);zip.flush();zip.closeEntry();}catch (Exception e) {throw new ServiceException(e.getMessage());}}IoUtil.close(zip);// 生成zip文件// 由于前面已经开启了response的outputstream,所以这里不能调用reset()方法,否则会导致流异常关闭
//        response.reset();response.addHeader("Access-Control-Allow-Origin", "*");response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");response.addHeader("Content-Length", "" + totalFileSize);response.setContentType("application/octet-stream; charset=UTF-8");// 下载后的文件名可以由前端控制,此处由前端控制所以注释掉了/*if(StrUtil.isNotEmpty(zipName)){FileUtils.setAttachmentResponseHeader(response, zipName + ".zip");}*/}
其中的文件查询进度是可选的,可以让前端生成个任务id传到服务器
 controller层
@GetMapping("/downloadZipByTrainId/{trainId}")public void downloadZipByTrainId(HttpServletResponse response, @PathVariable String trainId) throws IOException {// downloadZipByTrainId()方法里面调用downloadZip()this.iTrainMaterialsService.downloadZipByTrainId(trainId,response);}
前端代码,此处使用的是vue3
// 需要npm安装file-saver
import { saveAs } from 'file-saver'// 点击按钮触发事件
const clickDownLoadMaterials = async (row) =>{if(row.materialCount > 0 ){let res = await zip(`/train/trainMaterials/downloadZipByTrainId/${row.id}?projectName=${row.trainName}`,`${row.trainName}`)}else{ElMessage({message: '暂无材料可下载!',type: 'warning',})}
}const zip = (url, name) => {isShowLoadingDownLoadMaterial.value = trueurl = baseURL + urlaxios({method: 'get',url: url,responseType: 'blob',headers: {// 请求头token'Authorization': 'Bearer ' + getToken()}}).then(async (res) => {if(res.status == 200){isShowLoadingDownLoadMaterial.value = false}const isLogin = await blobValidate(res.data);if (isLogin) {const blob = new Blob([res.data], { type: 'application/zip' })saveAss(blob, name)} else {await this.printErrMsg(res.data);}})
}const saveAss = (text, name, opts) =>{saveAs(text, name, opts);
}// 验证是否为blob格式
export async function blobValidate(data) {try {const text = await data.text();JSON.parse(text);return false;} catch (error) {return true;}
}
杂谈
 这次的需求整理到了不少东西,一开始担心oom的问题,于是调试时改变了缓冲区大小,发现java使用的内存也会对应的发生变化,这是为什么呢?实际上原因很简单,final byte[] buffer = new byte[bufferSize];这个变量就是占用这么多字节的内存呀!
当缓冲区设成0的时候,会发现读的特别特别慢,这是为什么呢?这是由于读的大小太小了,java调取磁盘io的频率变多,而调取磁盘io是一个特别消耗资源的行为也特别慢,因此就慢了。设置合适的缓冲区大小,可以一次性读取对应大小的内容从而减少磁盘io的交互从而提升效率,但也要避免缓冲区过大导致变量占用内存过大而oom。
同时,java流的操作不是一次性将整个文件都读入内存中的,而是仅仅获取文件的句柄,同时读偏移量,不停的读,移动偏移量,直至文件读写结束(大佬原话)。而且系统加载文件到内存也是按块加载的,并不会一次性全读到内存中(如一个几十g的游戏,打开并不会占用几十g内存,而是用到的时候在加载,不足的时候就会根据算法按页或者按块替换)
最后,开了流一定要关流!response.getOutputStream()自带的流可以不用手动关servlet在请求结束的时候会自己关掉
相关文章:
java将文件压缩打包后进行下载
今天受到一个需求,需要查出文件,然后将文件打包后下载。看了下项目里默认代码有压缩功能,以此修改了下,项目使用了hutool。项目是若依项目 定义zip的数据传输对象,ossId可以是文件表的id Data public class SysOssZi…...
 
【4/26-4/30】 Arxiv安全类文章速览
4/26 标题: Merchants of Vulnerabilities: How Bug Bounty Programs Benefit Software Vendors 作者: Esther Gal-Or, Muhammad Zia Hydari, Rahul Telang摘要: 软件漏洞允许恶意黑客利用,威胁系统和数据安全。本文研究了激励道德黑客发现并负责任地向软件供应商披…...
 
活动图与状态图:UML中流程图的精细化表达——专业解析系统动态性与状态变迁
流程图是一种通用的图形表示法,用以展示步骤、决策和循环等流程控制结构。它通常用于描述算法、程序执行流程或业务过程,关注于任务的顺序执行。流程图强调顺序、分支和循环,适用于详细说明具体的处理步骤,图形符号相对基础和通用…...
Easy TCP Analysis提供了四大特性,兼顾了TCP数据包分析入门学习到实战问题排查不同阶段用户对工具的需求
一款兼顾TCP数据包分析入门学习和实战不同阶段用户需求的工具 Easy TCP Analysis是一款在线TCP数据包分析工具,致力于让TCP数据包分析变得跟看聊天记录一样简单! Easy TCP Analysis提供了四大特性,兼顾了从入门学习到实战分析不同阶段用户对…...
【2】STM32·FreeRTOS·任务创建和删除
目录 一、任务创建和删除的API函数 1.1、动态创建任务函数 1.2、静态创建任务函数 1.3、任务删除函数 二、任务创建和删除(动态方法) 三、任务创建和删除(静态方法) 一、任务创建和删除的API函数 任务的创建和删除本质就是…...
 
日志审计系统在提高网络安全方面具有哪些重要的作用
随着信息技术的飞速发展,我们正处于一个高度互联、数据驱动的网络时代。在这个时代,日志审计系统作为网络安全和信息管理的重要工具,发挥着至关重要的作用。下面德迅云安全就详细介绍下关于日志审计系统在当今网络时代的重要性。 一、什么是日…...
 
二维泊松方程(Neumann+Direchliet边界条件)有限元Matlab编程求解|程序源码+说明文本
专栏导读 作者简介:工学博士,高级工程师,专注于工业软件算法研究本文已收录于专栏:《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现,并提供所有案例完整源码;2.单元…...
 
13_Scala面向对象编程_伴生对象
文章目录 1.伴生对象1.1 scala的一个性质,scala文件中的类都是公共的;1.2 scala使用object关键字也可以声明对象; 3.关于伴生对象和类4.权限修饰符,scala仅有private;5.伴生对象可以访问伴生类中的私有属性;6.案例7.伴…...
 
RS485空调系统到BACnet江森楼宇系统的高效整合攻略
智慧城市的每一栋建筑都在追求更高的能效与更佳的居住体验,而这一切的实现离不开强大且灵活的楼宇自动化系统。其中,协议转换网关作为连接不同设备的纽带,扮演着至关重要的角色。本文将以一个典型的商业综合体为例,揭秘BACnet协议…...
 
Springboot集成Redis操作缓存-06
Redis简介 Redis(Remote Dictionary Server),即远程字典服务,是一个开源的、使用ANSI C语言编写的、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 功能特点 数据结构丰富&#…...
【WPF】聊聊WPF中INotifyPropertyChanged [TOC]
聊聊WPF中INotifyPropertyChanged 文章目录 聊聊WPF中INotifyPropertyChanged一、INotifyPropertyChanged接口二、DataContext2.1/DataContext作用2.2/DataContext特性2.3/DataContext实例 三、INotifyPropertyChanged接口的几种实现方式3.1/简单INotifyPropertyChanged绑定3.2…...
 
SpringBoot Actuator未授权访问漏洞的解决方法
1. 介绍 Spring Boot Actuator 是一个用于监控和管理 Spring Boot 应用程序的功能模块。它提供了一系列生产就绪的功能,帮助你了解应用程序的运行状况,以及在运行时对应用程序进行调整。Actuator 使用了 Spring MVC 来暴露各种 HTTP 或 JMX 端点&#x…...
 
AI大模型探索之路-训练篇18:大语言模型预训练-微调技术之Prompt Tuning
系列篇章💥 AI大模型探索之路-训练篇1:大语言模型微调基础认知 AI大模型探索之路-训练篇2:大语言模型预训练基础认知 AI大模型探索之路-训练篇3:大语言模型全景解读 AI大模型探索之路-训练篇4:大语言模型训练数据集概…...
 
Ollamallama
Olllama 直接下载ollama程序,安装后可在cmd里直接运行大模型; llama 3 meta 开源的最新llama大模型; 下载运行 1 ollama ollama run llama3 2 github 下载仓库,需要linux环境,windows可使用wsl; 接…...
 
苹果Mac用户下载VS Code(Universal、Intel Chip、Apple Silicon)哪个版本?
苹果macOS用户既可以下载通用版(Universal),软件将自动检测用户的处理器并进行适配。 也可以根据型号下载对应CPU的版本: 使用Intel CPU的Mac电脑可下载Intel Chip版本; 使用苹果自研M系列CPU的Mac电脑下载Apple Si…...
Linux(Ubuntu)安装CGAL(非root)
一、安装boost 下载地址:Boost C Libraries - Browse /boost at SourceForge.net 我安装的是1.77.0的版本 ./bootstrap.sh --prefix/usr/local/boost ./b2 ./b2 install 配置环境变量 vim ~/.bashrcexport BOOST_INCLUDE/usr/local/boost/include export BO…...
 
hadoop学习---基于Hive的教育平台数据仓库分析案例(三)
衔接第一部分,第一部分请点击:基于Hive的教育平台数据仓库分析案例(一) 衔接第二部分,第二部分请点击:基于Hive的教育平台数据仓库分析案例(二) 学生出勤模块(全量分析):…...
 
RAFT:引领 Llama 在 RAG 中发展
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
 
上海亚商投顾:沪指缩量调整 合成生物概念股持续爆发
上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 沪指昨日缩量震荡调整,深成指、创业板指均跌超1%。细胞免疫治疗概念股大涨,冠昊生物、…...
 
Maven+Junit5 + Allure +Jenkins 搭建 UI 自动化测试实战
文章目录 效果展示Junit 5Junit 5 介绍Junit 5 与 Junit 4 对比PageFactory 模式编写自动化代码公共方法提取测试用例参数化Jenkins 搭建及配置参数化执行生成 Allure 报告Maven 常用命令介绍POM 文件效果展示 本 chat 介绍 UI 自动化测试框架的搭建: 运用 page factory 模式…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
 
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
 
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
 
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
 
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
 
接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
 
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
 
9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...
