ZipUtil压缩工具类坑点

news/2024/11/16 18:32:18/

工具类包名:cn.hutool.core.util.ZipUtil

场景:需要将本地的文件以流的形式压缩并传给前端(本意是想着如果压缩成文件,还得开一个InputStream来读,性能可能会下降,实验结论在后文)

问题发现

第一次直接调用ZipUtil的public static void zip(OutputStream out, Charset charset, boolean withSrcDir, FileFilter filter, File... srcFiles)方法

脱敏代码如下:

HttpServletResponse response = getResponse();
response.setHeader("Content-disposition","attachment;filename=" + URLEncoder.encode("testOutPut", "UTF-8") + ".zip");
File file = new File("C:\\Users\\win\\Desktop\\新建文本文档.txt");ServletOutputStream outputStream = response.getOutputStream();
ZipUtil.zip(outputStream, Charset.defaultCharset(),Boolean.FALSE,null,file);

遇到的问题:前端下载正常,使用win10资源管理器打开压缩包显示 Windows 无法打开文件夹。“压缩(zipped)文件夹”C:\Users\win\Downloads\testOutPut.zip无效。

面向百度编程后,得到线索:压缩文件结尾有额外的异常文件,所以打不开,解决方案是关闭输出流。

但是我是从前端拿到的输出流,根据谁创建谁关闭原则,不应该关闭这个流。(当然我尝试过了,outputStream.close()还是同样的问题)

神奇的是,只有资源管理器不能打开该压缩包,使用360压缩可以正常解压,使用7-zip虽然打开和解压会报错,但是依然正常解压出文件。

第二次调用ZipUtil的public static void zip(ZipOutputStream zipOutputStream, boolean withSrcDir, FileFilter filter, File... srcFiles)方法

脱敏代码如下:

ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
ZipUtil.zip(zipOutputStream,false,null,file);
zipOutputStream.close();

此时zip恢复正常,资源管理器可以正常打开。

问题溯源

在这里插入图片描述

  1. 第一个参数为OutputStream的方法本质上在内部将其包装成ZipOutputStream后再调用方法二
    在这里插入图片描述
  2. 查看getZipOutputStream方法,其实就是判断是否为ZipOutputStream,否则new一个,将传入的流进行包装

到了这一步,我的想法是他创建了一个流,但是内部没有任何地方关闭它,且外部也没办法关闭,这样理论上不就违反了谁创建谁关闭原则吗?

后来经过老师指点,其实这个地方的思想是装饰者模式,本质上只是将传入的流进行包装,不是自己创建的新流,自然也不需要他关闭。

不过新的问题又出现了,按上述结论,zipOutputStream是不应该被关闭的,我为什么不加close就生成错误的zip,加了反而能正确运行呢?

// ZipOutputStream.java的close方法实际上调用的是他的父类DeflaterOutputStream.java的close
/*** Closes the ZIP output stream as well as the stream being filtered.* @exception ZipException if a ZIP file error has occurred* @exception IOException if an I/O error has occurred*/
public void close() throws IOException {if (!closed) {super.close();closed = true;}
}
// DeflaterOutputStream.java的close方法源码如下
/*** Writes remaining compressed data to the output stream and closes the* underlying stream.* @exception IOException if an I/O error has occurred*/
public void close() throws IOException {if (!closed) {finish();if (usesDefaultDeflater)def.end();out.close();closed = true;}
}

可以清楚的看到,DeflaterOutputStream.java的close方法先执行了finish()方法,再对流进行关闭。(同时修改关闭标志)

结论

实际上是流的finish()生效,每次创建一个压缩文件流时,需要finish()来标识该文件流已达到末尾,资源管理器在读到带结束标识的压缩文件才会正确打开(第三方压缩软件估计做了兼容处理)

而ZipUtil工具类中,作者直接new ZipOutputStream(ouputStream),却没有在任何地方调用finish()方法来标识结束,导致当传入一个不需要关闭的Stream的时候,无法正确标识结束

解决方案

当你在传入一个不希望被关闭的Stream时,请在外部自行new一个ZipOutputStream对象并传入该Stream,调用完ZipUtil的方法后,手动调用ZipOutputStream的finish()方法来标识


http://www.ppmy.cn/news/190445.html

相关文章

Java Zip UnZip

压缩和解压,一般可以使用如下两种方式: java Util 中提供的工具类Apache 中提供的工具类 java里面有个包叫java.util.zip提供zip文件压缩,但是编码的时候非常不方便。编码量太多了,通过搜索,发现apache有个包提供一些…

unzip报错 extra bytes at beginning or within zipfile (attempting to process anyway)

Shell脚本unzip报错 命令行自己解压没问题,但是脚本就报错。 ##报错如下 Archive: /mnt/home4T/day/BD-2017-04-01.zip warning [/mnt/home4T/day/BD-2017-04-01.zip]: 12331803155 extra bytes at beginning or within zipfile(attempting to process anyway) er…

无法完成压缩(zipped)文件来提取向导,怎么解决

1.找到这个高姿态的 压缩文件 右击 属性 点击 安全 点击 编辑 2.选择自己用户名 3.点击 点击应用 再确定 完美解决 ( •̀ ω •́ )y

Python zip, unzip, zip_longest的用法

目录 0. 前言 1. zip()有什么好处? 2. zip基本使用方法 2.1 语法 2.2 使用例 3. 反向操作:unzip 4. zip_longest() 0. 前言 本文简单介绍python中的zip()方法的使用,并相应介绍与之相关联的itertools模块中的zip_longest()。 简而言之&…

zip和unzip用法

zip 1.功能作用:压缩文件或者目录 2.位置:/usr/bin/zip 3.格式用法:zip [-options] [-b path] [-t mmddyyyy] [-n suffixes] [zipfile list] [-xi list] 4.主要参数 -f 更新现有的文件 -u 与-f参数类似,但是除了更新现有的文件外&…

多传感器融合SLAM --- 9.LIO-SAM如何运行、运行节点介绍

目录 1 LIO-SAM如何运行起来的 1.1 run.launch --- LIO-SAM主节点 1.2 module_loam.launch ---- 代码!启动!

zip用法

在学习《python编程 从入门到实践》这本书时,在16章‘收盘价均值’这里的代码遇到了问题,写下此笔记总结一下zip的用法 (菜鸟自己靠代码测试做的总结,如有错误,烦请指正)。 【注】python中,以中…

测试进阶必备,这5款http接口自动化测试工具真的很香

现在市场上能做接口自动化测试的工具有很多,一搜一大把,让人眼花缭乱。我们去选择对应实现方式时,不管是框架体系还是成熟稳定的工具,核心目的都是期望引入的技术能在最低投入的情况下达到最优效果。 那么我们选择依据出来了&…