简介

这几天在做通过流下载zip文件以及上传zip文件不解压读取zip文件内容的功能,在读取zip文件内容的时候遇到了size为-1的情况,在此记录下遇到的情况、解决办法、以及未解决的问题。

示例

将上传和下载zip文件的功能做成了一个示例,放到了github上,链接:export-import-zip-use-stream,可以尝试运行下。

遇到的问题

通过流下载zip文件之后,再次导入该zip文件,不解压读取zip文件内容,发现ZipEntry的size()返回-1,如下图所示:

但是尝试使用系统自带的压缩软件压缩了一个zip文件,并上传读取,发现一切正常,size不为-1。使用zipinfo命令查看两个文件作为对比,如下:

可以看到上面文件是通过导出功能生成的,红框里缺少size。而下面的是系统压缩软件压缩的zip文件,红框里面带有size大小。故猜测可能是由于代码里生成ZipEntry的时候没有设置size,compressize,crc32等属性的原因。

读取zip文件时ZipEntry的size为-1解决办法

直接读取当前ZipEntry的流,直到为-1为止,代码如下:

@PostMapping("import")
@ResponseBody
public void importZip(@RequestParam("file")MultipartFile file) throws IOException {ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream());ZipEntry zipEntry;while ((zipEntry = zipInputStream.getNextEntry()) != null) {if (zipEntry.isDirectory()) {// do nothing}else {String name = zipEntry.getName();long size = zipEntry.getSize();// unknown size// ZipEntry的size可能为-1,表示未知// 通过上面的几种方式下载,就会产生这种情况if (size == -1) {ByteArrayOutputStream baos = new ByteArrayOutputStream();while (true) {int bytes = zipInputStream.read();if (bytes == -1) break;baos.write(bytes);}baos.close();System.out.println(String.format("Name:%s,Content:%s",name,new String(baos.toByteArray())));} else { // ZipEntry的size正常byte[] bytes = new byte[(int) zipEntry.getSize()];zipInputStream.read(bytes, 0, (int) zipEntry.getSize());System.out.println(String.format("Name:%s,Content:%s",name,new String(bytes)));}}}zipInputStream.closeEntry();zipInputStream.close();
}

此时可以正确读取文件内容。

下载文件时设置size的解决办法

需要设置ZipEntry的size,compresSize以及crc32等属性。

例如:

public static void main(String[] args) throws IOException {File infile  = new File("test_file.pdf");File outfile = new File("test.zip");try (FileInputStream  fis = new FileInputStream(infile);FileOutputStream fos = new FileOutputStream(outfile);ZipOutputStream  zos = new ZipOutputStream(fos) ) {byte[]  buffer = new byte[1024];ZipEntry entry = new ZipEntry("data");precalc(entry, fis.getChannel());zos.putNextEntry(entry);for(int bytesRead; (bytesRead = fis.read(buffer)) >= 0; )zos.write(buffer, 0, bytesRead);zos.closeEntry();}try(FileInputStream fin = new FileInputStream(outfile);ZipInputStream  zis = new ZipInputStream(fin) ) {ZipEntry entry = zis.getNextEntry();System.out.println("Entry size: " + entry.getSize());System.out.println("Compressed size: " + entry.getCompressedSize());System.out.println("CRC: " + entry.getCrc());zis.closeEntry();}
}private static void precalc(ZipEntry entry, FileChannel fch) throws IOException {long uncompressed = fch.size();int method = entry.getMethod();CRC32 crc = new CRC32();Deflater def;byte[] drain;if(method != ZipEntry.STORED) {def   = new Deflater(Deflater.DEFAULT_COMPRESSION, true);drain = new byte[1024];}else {def   = null;drain = null;}ByteBuffer buf = ByteBuffer.allocate((int)Math.min(uncompressed, 4096));for(int bytesRead; (bytesRead = fch.read(buf)) != -1; buf.clear()) {crc.update(buf.array(), buf.arrayOffset(), bytesRead);if(def!=null) {def.setInput(buf.array(), buf.arrayOffset(), bytesRead);while(!def.needsInput()) def.deflate(drain, 0, drain.length);}}entry.setSize(uncompressed);if(def!=null) {def.finish();while(!def.finished()) def.deflate(drain, 0, drain.length);entry.setCompressedSize(def.getBytesWritten());}entry.setCrc(crc.getValue());fch.position(0);
}

另外一种解决方法,通过Enumeration

一、分析下原因

(PS:参考大佬的博客地址:https://blog.csdn.net/zbj18314469395/article/details/84109499)

二、解决办法:

把判断条件由【(ze=zin.getNextEntry())!=null】换成【zipEnum.hasMoreElements ()】
使用了枚举, Enumeration 接口(枚举)。

通常用 Enumeration 中的以下两个方法打印向量中的所有元素:

(1) boolean hasMoreElements(); // 是否还有元素,如果返回 true ,则表示至少含有一个元素

(2) public Object nextElement(); // 如果 Enumeration 枚举对象还含有元素,该方法返回对象中的下一个元素。如果没有,则抛出NoSuchElementException 异常。

附上我的代码,已经修改过的,大家可以参考一下

public void processFile(String path) throws Exception {ZipFile zf = new ZipFile(path);//主要是这块java.util.Enumeration zipEnum = zf.entries();InputStream in = new BufferedInputStream(new FileInputStream(path));ZipInputStream zin = new ZipInputStream(in, Charset.forName("gbk"));ZipEntry ze;//重点是是这块的判断while (zipEnum.hasMoreElements()) {ze = (ZipEntry) zipEnum.nextElement();if (!ze.getName().contains("__MACOSX") && !ze.getName().contains(".DS_Store")) {if (!ze.isDirectory()) {System.out.println("##name##" + ze.getName());System.out.println("##size##" + ze.getSize());}}}zin.closeEntry();}

另外一个例子:

https://github.com/yrom/shrinker/blob/master/shrinker/src/main/java/net/yrom/tools/JarProcessor.java

package net.yrom.tools;import com.google.common.collect.ImmutableList;import org.apache.commons.io.IOUtils;import org.apache.commons.io.output.ByteArrayOutputStream;import java.io.ByteArrayInputStream;import java.io.EOFException;import java.io.IOException;import java.io.OutputStream;import java.io.UncheckedIOException;import java.nio.file.Files;import java.nio.file.Path;import java.util.List;import java.util.function.Function;import java.util.stream.Collectors;import java.util.zip.CRC32;import java.util.zip.ZipEntry;import java.util.zip.ZipInputStream;import java.util.zip.ZipOutputStream;
/*** @author yrom* @version 2017/11/29*/
class JarProcessor extends ClassesProcessor {JarProcessor(Function<byte[], byte[]> classTransform, Path src, Path dst) {super(classTransform, src, dst);}@Overridepublic void proceed() {try {List<Pair<String, byte[]>> entryList = readZipEntries(src).parallelStream().map(this::transformClassBlob).collect(Collectors.toList());if (entryList.isEmpty()) return;try (OutputStream fileOut = Files.newOutputStream(dst)) {ByteArrayOutputStream buffer = zipEntries(entryList);buffer.writeTo(fileOut);}} catch (IOException e) {throw new RuntimeException("Reading jar entries failure", e);}}private ByteArrayOutputStream zipEntries(List<Pair<String, byte[]>> entryList) throws IOException {ByteArrayOutputStream buffer = new ByteArrayOutputStream(8192);try (ZipOutputStream jar = new ZipOutputStream(buffer)) {jar.setMethod(ZipOutputStream.STORED);final CRC32 crc = new CRC32();for (Pair<String, byte[]> entry : entryList) {byte[] bytes = entry.second;final ZipEntry newEntry = new ZipEntry(entry.first);newEntry.setMethod(ZipEntry.STORED); // chose STORED methodcrc.reset();crc.update(entry.second);newEntry.setCrc(crc.getValue());newEntry.setSize(bytes.length);writeEntryToJar(newEntry, bytes, jar);}jar.flush();}return buffer;}private Pair<String, byte[]> transformClassBlob(Pair<String, byte[]> entry) {byte[] bytes = entry.second;entry.second = classTransform.apply(bytes);return entry;}private List<Pair<String, byte[]>> readZipEntries(Path src) throws IOException {ImmutableList.Builder<Pair<String, byte[]>> list = ImmutableList.builder();try (ZipInputStream zip = new ZipInputStream(new ByteArrayInputStream(Files.readAllBytes(src)))) {for (ZipEntry entry = zip.getNextEntry();entry != null;entry = zip.getNextEntry()) {String name = entry.getName();if (!name.endsWith(".class")) {
// skipcontinue;}long entrySize = entry.getSize();if (entrySize >= Integer.MAX_VALUE) {throw new OutOfMemoryError("Too large class file " + name + ", size is " + entrySize);}byte[] bytes = readByteArray(zip, (int) entrySize);list.add(Pair.of(name, bytes));}}return list.build();}private byte[] readByteArray(ZipInputStream zip, int expected) throws IOException {if (expected == -1) { // unknown sizereturn IOUtils.toByteArray(zip);}final byte[] bytes = new byte[expected];int read = 0;do {int n = zip.read(bytes, read, expected - read);if (n <= 0) {break;}read += n;} while (read < expected);if (expected != bytes.length) {throw new EOFException("unexpected EOF");}return bytes;}private static void writeEntryToJar(ZipEntry entry, byte[] bytes, ZipOutputStream jar) {try {jar.putNextEntry(entry);jar.write(bytes);jar.closeEntry();} catch (IOException e) {throw new UncheckedIOException(e);}}// mutable pairstatic class Pair<F, S> {F first;S second;Pair(F first, S second) {this.first = first;this.second = second;}static <F, S> Pair<F, S> of(F first, S second) {return new Pair<>(first, second);}}
}

zip例子:

https://www.programcreek.com/java-api-examples/?code=FutureSonic/FutureSonic-Server/FutureSonic-Server-master/futuresonic-main/src/main/java/net/sourceforge/subsonic/controller/DownloadController.java

/*** Computes the CRC checksum for the given file.** @param file The file to compute checksum for.* @return A CRC32 checksum.* @throws IOException If an I/O error occurs.*/private static long computeCrc(File file) throws IOException {CRC32 crc = new CRC32();InputStream in = new FileInputStream(file);try {byte[] buf = new byte[8192];int n = in.read(buf);while (n != -1) {crc.update(buf, 0, n);n = in.read(buf);}} finally {in.close();}return crc.getValue();}private static void copyFileToStream(File file, OutputStream out) throws IOException {//LOG.info("Downloading '" + FileUtil.getShortPath(file) + "' to " + status.getPlayer());final int bufferSize = 16 * 1024; // 16 KbitInputStream in = new BufferedInputStream(new FileInputStream(file), bufferSize);try {byte[] buf = new byte[bufferSize];long bitrateLimit = 0;long lastLimitCheck = 0;while (true) {long before = System.currentTimeMillis();int n = in.read(buf);if (n == -1) {break;}out.write(buf, 0, n);}} finally {out.flush();in.close();}}private static void zip(ZipOutputStream out, String root, File file) throws IOException {// Exclude all hidden files starting with a "."if (file.getName().startsWith(".")) {return;}String zipName = file.getCanonicalPath().substring(root.length() + 1);if (file.isFile()) {ZipEntry zipEntry = new ZipEntry(zipName);zipEntry.setSize(file.length());zipEntry.setCompressedSize(file.length());zipEntry.setCrc(computeCrc(file));out.putNextEntry(zipEntry);copyFileToStream(file, out);out.closeEntry();} else {ZipEntry zipEntry = new ZipEntry(zipName + '/');zipEntry.setSize(0);zipEntry.setCompressedSize(0);zipEntry.setCrc(0);out.putNextEntry(zipEntry);out.closeEntry();File[] children = file.listFiles(); //FileUtil.listFiles(file);for (File child : children) {zip(out, root, child);}}}public static void zipDir(String zipFileName, String dir) {try {File zipFile = new File(zipFileName);FileOutputStream fos = new FileOutputStream(zipFile);//ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos));CheckedOutputStream checksum = new CheckedOutputStream(fos, new CRC32());ZipOutputStream zos = new ZipOutputStream(checksum);File dirFile = new File(dir);String root = zipFile.getCanonicalPath().substring(0, zipFile.getCanonicalPath().length() - zipFile.getName().length() - 1);zip(zos, root, dirFile);} catch (IOException e) {e.printStackTrace();}}

但是上面的zip方法,压缩zip后,用Zip4J解压会失败,文件也有问题。这个后面来研究。

转自:

http://cxis.me/2018/03/03/%E4%B8%8A%E4%BC%A0Zip%E6%96%87%E4%BB%B6%E4%B8%8D%E8%A7%A3%E5%8E%8B%E8%AF%BB%E5%8F%96%E6%96%87%E4%BB%B6%E5%86%85%E5%AE%B9%E6%97%B6ZipEntry%E7%9A%84size%E4%B8%BA-1%E7%9A%84%E9%97%AE%E9%A2%98/

Sprintboot 解压Zip文件,ZipEntry的zipEntry.getSize()为-1的问题相关推荐

  1. 【Android 安全】DEX 加密 ( 代理 Application 开发 | 解压 apk 文件 | 判定是否是第一次启动 | 递归删除文件操作 | 解压 Zip 文件操作 )

    文章目录 一.判定是否是第一次启动 二.递归删除文件操作 三.解压 Zip 文件操作 四.解压操作相关代码 参考博客 : [Android 安全]DEX 加密 ( 常用 Android 反编译工具 | ...

  2. Android 解压zip文件

    过了n多天后,当再次使用原先博客上写的那篇: Android 压缩解压zip文件 去做zip包的解压的时候,出现了原来没有发现的很多问题.首先是中文汉字问题,使用java的zip包不能很好的解决解压问 ...

  3. Android 解压zip文件你知道多少?

    对于Android常用的压缩格式ZIP,你了解多少? Android的有两种解压ZIP的方法,你知道吗? ZipFile和ZipInputStream的解压效率,你对比过吗? 带着以上问题,现在就开始 ...

  4. 【Web技术】959- JavaScript 如何在线解压 ZIP 文件?

    相信大家对 ZIP 文件都不会陌生,当你要打开本地的 ZIP 文件时,你就需要先安装支持解压 ZIP 文件的解压软件.但如果预解压的 ZIP 文件在服务器上,我们应该如何处理呢? 最简单的一种方案就是 ...

  5. java解压zip文件,处理文件名不能为中文

    1.最近工作需要把压缩文件解压,经过测试有两种方法,一种是JDK自带的ZipFile,       另外一种是org.apache.tools.zip进行解压. 2.经测试,JDK自带的文件不能处理文 ...

  6. android zip格式应用,Android 压缩解压zip文件

    Android 压缩解压zip文件 上次写了个解压缩功能,但有局限性,比如压缩文件xx.zip 里包括子目录的情况下,执行上次解压缩的功能就不能实现我们想要的效果,于是在网上参考了一下java的解压缩 ...

  7. 解压ZIP文件工具类

    解压ZIP文件工具类 package com.xx;import org.springframework.web.multipart.MultipartFile;import java.io.File ...

  8. java 解压文件_java实现解压zip文件,(亲测可用)!!!!!!

    项目结构: Util.java内容: package com.cfets.demo; import java.io.File; import java.io.FileOutputStream; imp ...

  9. Java 压缩与解压zip文件

    一.压缩文件大致可以分为三种:ZIP.JAR.GZ. 压缩流 在日常中经常会使用到像WinRAR或WinZIP这样的压缩文件,通过这些软件可以把一个很大的文件进行压缩以方便传输. 在JAVA中,为了减 ...

  10. java解压zip文件

    package com.chuangqi.tools;import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipFile ...

最新文章

  1. python默认数据类型转换_Python 数据类型转换
  2. 【Groovy】MOP 元对象协议与元编程 ( 使用 Groovy 元编程进行函数拦截 | 动态拦截函数 | 动态获取 MetaClass 中的方法 | evaluate 方法执行Groovy脚本 )
  3. CISCO交换机如何删除 Vlan
  4. MySQL-InnoDB究竟如何巧妙实现,4种事务的隔离级别
  5. helm部署hadoop并指定namespace和名称的命令
  6. 微软.Net开发中的多线程编程总结
  7. 素数与线性筛选法初级版
  8. Cocos数据篇[3.4](4) ——plist文件操作
  9. java报错空指针异常_分析使用Spring Boot进行单元测试时,报出空指针异常
  10. 在ubuntu - linux系统下装TensorFlow(虚拟机)
  11. 电子双缝干涉,可以在穿过缝前进行探测
  12. 2016 1月1日-1月1日python 学习总结
  13. python怎么导入sql数据库_如何用Python3写一段将Excel数据导入SQL数据库?
  14. 苏州大学计算机复试python_苏州大学计算机考研复试经验总结
  15. 解决网页微信扫码登录报40163
  16. win7如何开启无线网卡服务器,大神教你win7无线网卡怎么设置wifi
  17. 做网站需要哪些费用?(维护方面)
  18. 如何禁止视频在手机移动端页面中全屏播放
  19. ResNeXt算法详解(resnet提升篇)
  20. [Power BI] 认识Power Query和M语言

热门文章

  1. RFC2544优化步长测试——信而泰网络测试仪实操
  2. NODE.JS菜鸟网总结
  3. 电脑系统没有自带的字体-楷体GB2312字体 免费版提供下载
  4. 音视频入门系列-字幕篇(SSA ASS)
  5. 卡通漫画Photobacks Cartoon 2.0 PS扩展面板汉化版 支持CC2019
  6. 思科SDN技术:ACI架构概述
  7. SPI总线-物理层 协议层
  8. 平板电脑黑苹果EFI_首次安装黑苹果系统,原来如此简单,比真正的Mac电脑更快...
  9. UWF自定义设置与命令管理
  10. 在IEEE 上发表会议论文需要注意的几个事情