最新要闻
- 神舟十六号发射圆满成功:大红屏再次点亮 环球新资讯
- 全脂/低脂可选:特仑苏纯牛奶2.7元/盒大促(商超6元)
- 中科院院士:电动车主可以往外卖电 一年能卖4000元-焦点简讯
- 世界短讯!“十项全能”是怎样一种体验?华为Mate X3绝对是大屏折叠旗舰天花板
- 世界视点!小米MIX Fold 3曝光:屏下摄像头+潜望长焦 再无短板
- 每日热讯!电力板块异动拉升 世茂能源两连板
- 最野性的福特SUV!探险者昆仑巅峰版上市:售39.98万
- 新资讯:媒体评“剩菜盲盒”被疯抢 越来越多年轻人喜爱:便宜、杜绝浪费食品
- 荣耀赵明:MagicOS 8.0会有巨大进步 某些层面跟鸿蒙流畅性不相上下-环球快资讯
- 天天实时:给老外来点中国车 比亚迪ATTO 3成海外多国销冠:跻进全球前10
- 每日简讯:PS5国内白菜价:但PS5游戏盘开始涨价了
- 天天快资讯丨找数字专注力训练(找数字)
- 每日头条!刚被微软夸史上最可靠系统 Win11又惹祸:AMD显卡中招
- 天天讯息:知名电竞解说Yammers自杀 留遗书称曾遭霸凌:玩家晒视频怀念 再见了宝贝
- 国产电商二手剪辑卖货 拿捏欧美老外
- 女子用餐被收700元服务费却无服务 官方回应1对1管家服务:网友感慨
广告
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
Java实现打包压缩文件或文件夹生成zip以实现多文件批量下载
(资料图片)
有时候在系统中需要一次性下载多个文件,但逐个下载文件比较麻烦。这时候,最好的解决办法是将所有文件打包成一个压缩文件,然后下载这个压缩文件,这样就可以一次性获取所有所需的文件了。
下面是一个名为CompressUtil的工具类的代码,它提供了一些方法来处理文件压缩和下载操作:
import org.apache.commons.lang3.ArrayUtils;import org.apache.commons.lang3.StringUtils;import org.apache.lucene.util.RamUsageEstimator;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.*;import java.util.*;import java.util.zip.*;/** * @author fhey * @date 2023-05-11 20:48:28 * @description: 压缩工具类 */public class CompressUtil { private static final Logger logger = LoggerFactory.getLogger(CompressUtil.class); /** * 将文件打包到zip并创建文件 * * @param sourceFilePath * @param zipFilePath * @throws IOException */ public static void createLocalCompressFile(String sourceFilePath, String zipFilePath) throws IOException { createLocalCompressFile(sourceFilePath, zipFilePath, null); } /** * 将文件打包到zip并创建文件 * * @param sourceFilePath * @param zipFilePath * @param zipName * @throws IOException */ public static void createLocalCompressFile(String sourceFilePath, String zipFilePath, String zipName) throws IOException { File sourceFile = new File(sourceFilePath); if (!sourceFile.exists()) { throw new RuntimeException(sourceFilePath + "不存在!"); } if(StringUtils.isBlank(zipName)){ zipName = sourceFile.getName(); } File zipFile = createNewFile(zipFilePath + File.separator + zipName + ".zip"); try (FileOutputStream fileOutputStream = new FileOutputStream(zipFile)) { compressFile(sourceFile, fileOutputStream); } } /** * 获取压缩文件流 * * @param sourceFilePath * @return ByteArrayOutputStream * @throws IOException */ public static OutputStream compressFile(String sourceFilePath, OutputStream outputStream) throws IOException { File sourceFile = new File(sourceFilePath); if (!sourceFile.exists()) { throw new RuntimeException(sourceFilePath + "不存在!"); } return compressFile(sourceFile, outputStream); } /** * 获取压缩文件流 * * @param sourceFile * @return ByteArrayOutputStream * @throws IOException */ private static OutputStream compressFile(File sourceFile, OutputStream outputStream) throws IOException { try (CheckedOutputStream checkedOutputStream = new CheckedOutputStream(outputStream, new CRC32()); ZipOutputStream zipOutputStream = new ZipOutputStream(checkedOutputStream)) { doCompressFile(sourceFile, zipOutputStream, StringUtils.EMPTY); return outputStream; } } /** * 处理目录下的文件 * * @param sourceFile * @param zipOutputStream * @param zipFilePath * @throws IOException */ private static void doCompressFile(File sourceFile, ZipOutputStream zipOutputStream, String zipFilePath) throws IOException { // 如果文件是隐藏的,不进行压缩 if (sourceFile.isHidden()) { return; } if (sourceFile.isDirectory()) {//如果是文件夹 handDirectory(sourceFile, zipOutputStream, zipFilePath); } else {//如果是文件就添加到压缩包中 try (FileInputStream fileInputStream = new FileInputStream(sourceFile)) { //String fileName = zipFilePath + File.separator + sourceFile.getName(); String fileName = zipFilePath + sourceFile.getName(); addCompressFile(fileInputStream, fileName, zipOutputStream); //String fileName = zipFilePath.replace("\\", "/") + "/" + sourceFile.getName(); //addCompressFile(fileInputStream, fileName, zipOutputStream); } } } /** * 处理文件夹 * * @param dir 文件夹 * @param zipOut 压缩包输出流 * @param zipFilePath 压缩包中的文件夹路径 * @throws IOException */ private static void handDirectory(File dir, ZipOutputStream zipOut, String zipFilePath) throws IOException { File[] files = dir.listFiles(); if (ArrayUtils.isEmpty(files)) { ZipEntry zipEntry = new ZipEntry(zipFilePath + dir.getName() + File.separator); zipOut.putNextEntry(zipEntry); zipOut.closeEntry(); return; } for (File file : files) { doCompressFile(file, zipOut, zipFilePath + dir.getName() + File.separator); } } /** * 获取压缩文件流 * * @param documentList 需要压缩的文件集合 * @return ByteArrayOutputStream */ public static OutputStream compressFile(List documentList, OutputStream outputStream) { Map> documentMap = new HashMap<>(); documentMap.put("", documentList); return compressFile(documentMap, outputStream); } /** * 将文件打包到zip * * @param documentMap 需要下载的附件集合 map的key对应zip里的文件夹名 * @return ByteArrayOutputStream */ public static OutputStream compressFile(Map> documentMap, OutputStream outputStream) { CheckedOutputStream checkedOutputStream = new CheckedOutputStream(outputStream, new CRC32()); ZipOutputStream zipOutputStream = new ZipOutputStream(checkedOutputStream); try { for (Map.Entry> documentListEntry : documentMap.entrySet()) { String dirName = documentMap.size() > 1 ? documentListEntry.getKey() : ""; Map fileNameToLen = new HashMap<>();//记录单个合同号文件夹下每个文件名称出现的次数(对重复文件名重命名) for (FileInfo document : documentListEntry.getValue()) { try { //防止单个文件夹下文件名重复 对重复的文件进行重命名 String documentName = document.getFileName(); if (fileNameToLen.get(documentName) == null) { fileNameToLen.put(documentName, 1); } else { int fileLen = fileNameToLen.get(documentName) + 1; fileNameToLen.put(documentName, fileLen); documentName = documentName + "(" + fileLen + ")"; } String fileName = documentName + "." + document.getSuffix(); if (StringUtils.isNotBlank(dirName)) { fileName = dirName + File.separator + fileName; } addCompressFile(document.getFileInputStream(), fileName, zipOutputStream); } catch (Exception e) { logger.info("filesToZip exception :", e); } } } } catch (Exception e) { logger.error("filesToZip exception:" + e.getMessage(), e); } return outputStream; } /** * 将单个文件写入文件压缩包 * * @param inputStream 文件输入流 * @param fileName 文件在压缩包中的相对全路径 * @param zipOutputStream 压缩包输出流 */ private static void addCompressFile(InputStream inputStream, String fileName, ZipOutputStream zipOutputStream) { try (BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)) { ZipEntry zipEntry = new ZipEntry(fileName); zipOutputStream.putNextEntry(zipEntry); byte[] bytes = new byte[1024]; int length; while ((length = bufferedInputStream.read(bytes)) >= 0) { zipOutputStream.write(bytes, 0, length); zipOutputStream.flush(); } zipOutputStream.closeEntry(); //System.out.println("map size, value is " + RamUsageEstimator.sizeOf(zipOutputStream)); } catch (Exception e) { logger.info("addFileToZip exception:", e); throw new RuntimeException(e); } } /** * 通过网络请求下载zip * * @param sourceFilePath 需要压缩的文件路径 * @param response HttpServletResponse * @param zipName 压缩包名称 * @throws IOException */ public static void httpDownloadCompressFile(String sourceFilePath, HttpServletResponse response, String zipName) throws IOException { File sourceFile = new File(sourceFilePath); if (!sourceFile.exists()) { throw new RuntimeException(sourceFilePath + "不存在!"); } if(StringUtils.isBlank(zipName)){ zipName = sourceFile.getName(); } try (ServletOutputStream servletOutputStream = response.getOutputStream()){ CompressUtil.compressFile(sourceFile, servletOutputStream); response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment; filename=\"" + zipName + ".zip\""); servletOutputStream.flush(); } } public static void httpDownloadCompressFileOld(String sourceFilePath, HttpServletResponse response, String zipName) throws IOException { try (ServletOutputStream servletOutputStream = response.getOutputStream()){ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] zipBytes = byteArrayOutputStream.toByteArray(); response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment; filename=\"" + zipName + ".zip\""); response.setContentLength(zipBytes.length); servletOutputStream.write(zipBytes); servletOutputStream.flush(); } } /** * 通过网络请求下载zip * * @param sourceFilePath 需要压缩的文件路径 * @param response HttpServletResponse * @throws IOException */ public static void httpDownloadCompressFile(String sourceFilePath, HttpServletResponse response) throws IOException { httpDownloadCompressFile(sourceFilePath, response, null); } /** * 检查文件名是否已经存在,如果存在,就在文件名后面加上“(1)”,如果文件名“(1)”也存在,则改为“(2)”,以此类推。如果文件名不存在,就直接创建一个新文件。 * * @param filename 文件名 * @return File */ public static File createNewFile(String filename) { File file = new File(filename); if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } else { String base = filename.substring(0, filename.lastIndexOf(".")); String ext = filename.substring(filename.lastIndexOf(".")); int i = 1; while (true) { String newFilename = base + "(" + i + ")" + ext; file = new File(newFilename); if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } break; } i++; } } return file; }}
FileInfo类代码:
/** * @author fhey * @date 2023-05-11 21:01:26 * @description: TODO */@Datapublic class FileInfo { private InputStream fileInputStream; private String suffix; private String fileName; private boolean isDirectory;}
测试压缩并在本地生成文件:
public static void main(String[] args) throws Exception { //在本地创建压缩文件 CompressUtil.createLocalCompressFile("D:\\书籍\\电子书\\医书", "D:\\test"); }
压缩并在本地生成文件验证结果:
压缩文件并通过http请求下载:
/** * @author fhey */@RestControllerpublic class TestController { @GetMapping(value = "/testFileToZip") public void testFileToZip(HttpServletResponse response) throws IOException { String zipFileName = "myFiles"; String sourceFilePath = "D:\\picture"; CompressUtil.httpDownloadCompressFile(sourceFilePath,response, zipFileName); }}
压缩文件并通过http请求下载验证结果:
关键词:
Java实现打包压缩文件或文件夹生成zip以实现多文件批量下载
大语言模型技术原理-环球视讯
MySQL之运算符大全|每日速讯
第八单元 数组与集合
(Java)记一次通过API递归分页“爬取”网页数据的开发经历
神舟十六号发射圆满成功:大红屏再次点亮 环球新资讯
全脂/低脂可选:特仑苏纯牛奶2.7元/盒大促(商超6元)
中科院院士:电动车主可以往外卖电 一年能卖4000元-焦点简讯
世界短讯!“十项全能”是怎样一种体验?华为Mate X3绝对是大屏折叠旗舰天花板
世界视点!小米MIX Fold 3曝光:屏下摄像头+潜望长焦 再无短板
每日热讯!电力板块异动拉升 世茂能源两连板
企业内训一体化解决方案,布道师教学实训云平台【开源版上线】|今日热闻
世界快报:JS中的事件监听
最野性的福特SUV!探险者昆仑巅峰版上市:售39.98万
新资讯:媒体评“剩菜盲盒”被疯抢 越来越多年轻人喜爱:便宜、杜绝浪费食品
荣耀赵明:MagicOS 8.0会有巨大进步 某些层面跟鸿蒙流畅性不相上下-环球快资讯
天天实时:给老外来点中国车 比亚迪ATTO 3成海外多国销冠:跻进全球前10
每日简讯:PS5国内白菜价:但PS5游戏盘开始涨价了
MongoDB学习笔记:配置文件-世界今热点
天涯神贴合集(2023最新)
天天快资讯丨找数字专注力训练(找数字)
每日头条!刚被微软夸史上最可靠系统 Win11又惹祸:AMD显卡中招
天天讯息:知名电竞解说Yammers自杀 留遗书称曾遭霸凌:玩家晒视频怀念 再见了宝贝
国产电商二手剪辑卖货 拿捏欧美老外
女子用餐被收700元服务费却无服务 官方回应1对1管家服务:网友感慨
如何强制删除电脑文件内容(如何强制删除电脑文件)
收益率持续下行 同业存单市场走牛
世界快资讯丨【读财报】19家民营银行财报透视:网商银行、华瑞银行、新网银行不良率居前三
无敌性价比、能灭Zen4全家桶 Intel 14核酷睿i5-13600KF到手1969元
SSD大牌被卷到!铠侠RC20史低价:1TB 299元/2TB 599元
【世界新要闻】神舟十六号即将发射!三位航天员亮相出征仪式:要出差半年
天天热讯:今年最强LCD手机!Redmi Note 12T Pro游戏实测:《王者》《原神》几乎满帧
全球观点:是否具有派遣资格是什么意思_是否具有派遣资格
读数据压缩入门笔记02_二进制和熵
计划2030年前实现中国人登月 马斯克:中国航天比多数人想象得要强大
女子骨折做手术后左腿变长2.3厘米:鉴定十级伤残 医院拒赔
产油大国起内讧:俄罗斯低价供应大量石油 沙特都顶不住了|独家焦点
5月29日基金净值:信诚量化阿尔法股票A最新净值1.4838,跌0.3% 全球通讯
找狗(关于找狗的基本详情介绍) 当前最新
有这么个神人:被毒蛇咬173次 长期注射蛇毒 活了101岁
Flash死了!但小时候玩过的游戏 还不想死
地铁上 女子充电宝突然爆炸!现场烟雾弥漫
今晚油价或迎年内第四涨!加满一箱多花3.5元
3300秒!我国未来载人登月火箭主力发动机单台试车时长创新纪录 焦点滚动
【环球热闻】111完美邮箱_1163 com邮箱
【港股通】国泰航空(00293)据报接近订购波音777-8F货机-环球时快讯
土耳其总统埃尔多安再次赢得大选,将再度连任,外交部回应
5月29日基金净值:南方荣光A最新净值1.523,涨0.07% 当前观点
里程碑博士_里程碑的意思_今日看点
速看:武汉洪山融创智谷举办首届菁英联谊会,青年男女开启甜蜜之旅
黄果树天星洞景区小孩敲断钟乳石 工作人员称会尽快处理进行追责
国产动漫《斗罗大陆》终于迎来大结局 堪称“电影级”观影体验
德国一模特通过断骨增高将自己的身高从163增加至180厘米 手术风险极大
阿根廷国家队公布最新一期球队大名单 将由梅西与迪玛利亚领衔
东航全球首架交付国产大飞机C919顺利抵达 全球首次商业载客飞行成功
天天观点:淘宝账号登陆失败怎么回事_我的淘宝账户登录
2023.5.29Linux系统压缩打包|今日看点
环球看点!蚕豆迎丰收 “公司+专业合作社+农户”模式助农增收
蒙牛乳业宁夏工厂正式落成投产 为全球首座乳业全数智化工厂-天天快报
华硕发布49英寸QD-OLED带鱼屏显示器:5K分辨率 1000nits亮度
热点评!童年回忆满满 《王者荣耀》三款电玩新皮肤来了
全球今亮点!能玩破解游戏!任天堂回应下架《海豚模拟器》
全球快看点丨红蜘蛛新一代校色仪Spyder X2/X2 Ultra发布:最高支持2000nit亮度
今日看点:12)自定义函数
文心一言 VS 讯飞星火 VS chatgpt (25)-- 算法导论4.2 7题 天天实时
【时快讯】100克黄焖鸡调料可以做几斤鸡肉?
Intel底层技术加持、腾讯应用宝登陆电脑:安卓应用完美移植PC 更有神器辅助 世界通讯
重回线下!2023 ChinaJoy官宣:7月28日上海见
全球快讯:让利16亿卖爆!小米618天猫预售战报出炉:订单金额超6亿拿下冠军
手机意外跌落损坏 小米工程师力保数据 女米粉写信感谢:还买小米 即时
全球要闻:SpringBoot集成Swagger3.0(详细)
世界速读:对话丨财通基金朱海东:市场情绪影响沪深300近期走势,仍看好A股
全球观点:19岁MIT辍学,26岁的他干出个500亿独角兽
Win11 23H2更新确认秋季推出!功能更新数量少了
世界微速讯:你会买么?调查显示超76%玩家不看好索尼新掌机
世界聚焦:Doris(三) -- Rollup和物化视图
《小美人鱼》在多国遭遇集体刷差评 女主假发造价15万美元
不是手游、不抽角色!国产大作《影之刃零》官宣为买断单机游戏|快播报
天天微动态丨日版任天堂Switch OLED版618抄底价:到手1817元
时讯:余承东放出豪言:问界年产上千万 做世界第一
快看:充满80%仅需30分钟!7座理想4C超级充电站上线
天天热点评!【一步步开发AI运动小程序】九、姿态辅助调试桌面工具折使用
刷爆的“信用卡”——起底美国债务危机_全球最资讯
每日聚焦:深交所:调整深证成指、创业板指、深证100等指数样本股
打造全国一流职教发展共同体,深职院与深圳二职签约合作办学
有梦想就了不起!高考307分女孩考研逆袭211:还将继续攻读博士
累计被扣78分 男子酒驾被查:自述“我平时是个老实人”
全球时讯:血糖健康开拓者!华为WATCH4 Pro评测:全面的腕上守护专家
女性出现这7个症状要小心尿路感染:再忙也要去医院看看!-世界速递
全球头条:山东一公司要求员工签自愿放弃社保承诺书 同事:不签担心被挤兑
2023天津和平四平东道小学转学通知
11)MySQL编程基础
【Oracle impdp/expdp】Big lesson from failure with impdp/expdp in 12c 焦点要闻
交易商协会:一季度债市信评调整次数下降 负面调整占七成-今日热讯
LCD小金刚!Redmi Note 12T Pro配备12GB大内存、5080mAh电池
iPhone 16 Pro机型屏幕为6.3和6.9英寸 为啥手机屏越来越大? 今日热讯
世界今日报丨2999元起 魅族20斩获京东预售销量TOP4:三年质保行业罕见
苹果WWDC 2023来了!iOS 17把短板都消灭干净了
618会员抄底:百度/腾讯/QQ/京东/B站/爱奇艺等大促合集-环球头条
东莞景湖装饰,家居装修达人_世界报道