工具类包名: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()方法来标识

ZipUtil压缩工具类坑点相关推荐

  1. java zip 压缩工具类

    java zip 压缩工具类 目录结构 环境依赖 zip压缩工具类 遇到的问题 环境依赖 <!--这里作者使用的是 springboot 2.3.2.RELEASE 版本--> <d ...

  2. Java 文件压缩工具类

    import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.io.*; import java.util.zip.ZipEn ...

  3. Java 文件压缩工具类(支持多级目录压缩)

    一.特点 提供了多个重载方法,适用于多种场景 支持压缩完成后返回输入流,方便文件下载时进行流对拷 支持对输入流进行压缩 支持对多级目录压缩 二.源码 /*** 文件压缩工具类** @author zh ...

  4. java图片压缩工具类

    java图片压缩工具类 PicCompressUtil.java import java.io.ByteArrayInputStream; import java.io.ByteArrayOutput ...

  5. android图片压缩工具类

    好久没写博客了,一方面是因为最近找了家实习单位,很累基本上下班后就没有打不起精神去学习,另一方面我自己觉得写博客确实有点耗时间,趁着周六周日想花点时间研究下fresco,picass,Glide等框架 ...

  6. Java文件压缩工具类ZipUtils学习笔记

    最近工作中用到了文件的压缩,经过在网上查询和自己的摸索总结如下工具了,支持文件压缩,嵌套文件压缩: package utils;import java.io.File; import java.io. ...

  7. 一个好用的android图片压缩工具类

    <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255) ...

  8. Zip压缩和解压缩工具类

    依赖第三方jar包:ant-1.7.1.jar /** * @Title: ZipUtils.java * @Package com.sz.mt.test.zip * @Description: TO ...

  9. 整合的图片压缩工具类ImageTools

    根据网上找的资料和自己用到的地方进行修改的图片压缩工具类,有什么不对的地方请见谅,源码如下: public final class ImageTools {/*** Transfer drawable ...

最新文章

  1. 查看dev下设备名的含义
  2. java源码之HashMap和HashTable的异同
  3. (转载)彻底理解浏览器的缓存机制
  4. 云炬VB开发笔记 1初始Visual Basic基础
  5. -bash:/etc/profile Permission Denied
  6. SAP CRM WebClient UI session restart
  7. TCPDUMP/LIBPCAP 2-搭建环境
  8. 离线安装mysql5.6及依赖_Linux离线安装mysql 5.6详细步骤
  9. 为什么用TreeView绑定XML文件时总是提示:根级别上的数据无效。
  10. 【VMCloud云平台进阶篇】应用层面优化
  11. php国家图书馆opac的marc,国家图书馆OPAC书目信息在图书馆编目中的应用
  12. Namecheap 给域名添加ssl证书
  13. DeepFool对抗算法_学习笔记
  14. 对不起,不知道这些,我劝你还是别做软件测试员了!
  15. [NodeJS] Jest 环境下 Axios 请求报错: Cross origin http://localhost forbidden
  16. (原)python中matplotlib的颜色及线条控制
  17. mac php7.2,MAC下更新自带的PHP版本到7.2
  18. 令牌登录方式流程(token)
  19. 容量因子k计算公式_K因子计算方法
  20. 数字化转型再下一城,数字孪生厂商优锘科技宣布完成超3亿元融资

热门文章

  1. 用java代码打印一首诗
  2. TL-WR740N设置WDS无线桥接模式
  3. java实现流量控制
  4. SelectKey returned more than one value
  5. HA historyserver错误日志java.lang.RuntimeException: java.io.IOException: Couldn‘t create proxy provid nu
  6. Ubuntu apt错误 E: Unable to locate packag *
  7. 未备案域名使用Cloudflare进行301重定向
  8. Linux系统组成以及应用docker场景
  9. 最新LVGL8.3.7版本汉字输入法的使用,使用自定义词典。
  10. 文艺复兴+英国现代教育