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 模式…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...