ZipArchiveInputStream报错Unexpected record signature: 0X8074B50问题记录

87 阅读3分钟

1、问题介绍

使用的是Aapche提供的文件压缩和解压缩的库,官方文档:apache/commons-compress: Apache Commons Compress (github.com)

这个问题我遇到时:解压报错,但是本地解压没问题,但是有的zip压缩包解压名没问题,感觉就很离谱,困扰了我一天,终于在这个库的issues找到了相关的帖子,也对于问题解决提供了一种思路(百度找不到,去官方找,但是这个不在github,而在issues.apache.org)。

相关代码:

        try (InputStream is = Files.newInputStream(p)) {
           try (ZipArchiveInputStream zais = new ZipArchiveInputStream(is)) {
                ZipArchiveEntry zae = zais.getNextZipEntry();
                while (zae != null) {
                    System.out.println("commons-compress stream: " + zae.getName());
                    zae = zais.getNextZipEntry();
                }
            }
        } catch (ZipException e) {
            e.printStackTrace();
        }

遇到的报错:

java.util.zip.ZipException: Unexpected record signature: 0X---- at org.apache.commons.compress.archivers.zip.ZipArchiveInputStream.getNextZipEntry(ZipArchiveInputStream.java:286) at org.apache.commons.compress.archivers.zip.ZipArchiveInputStream.getNextEntry(ZipArchiveInputStream.java:435)

官方解答:

[COMPRESS-506] ZipException when on ZipArchiveInputStream but not ZipFile - ASF JIRA (apache.org)

[COMPRESS-562] ZipArchiveInputStream fails with unexpected record signature while ZipInputStream from java.util.zip succeeds - ASF JIRA (apache.org)

1)mimetype的本地文件头中的通用位标志表明它没有数据描述符,实际上有一个。我们没有跳过mimetype的数据描述符。在读取content.xml(某个文件)时,我们实际上是在读取mime类型的数据描述符,因为我们没有事先跳过它,这导致了一个异常。

2)似乎是条形码/邮编的问题?仍然不确定zip归档文件是如何创建的。但在我看来,这不是Commons Compress的问题。这是创建zip文件的库的问题。你可以用ZipFile提取它。这是因为ZipFile和ZipArchiveInputStream在提取zip存档时有不同的机制——ZipFile基于中央文件头,而ZipArchiveInputStream基于本地文件头

2、我的理解

ZipArchiveInputStreamZipFile两种zip包存储的方式不同,ZipFile压缩的文件用ZipArchiveInputStream解压会有一些问题,两者的对比参考以下的文档:

commons.apache.org/proper/comm…

对比:

ZIP归档按顺序存储归档条目,并在归档的最后包含所有条目的注册表。一个存档包含多个同名的条目,并让注册中心(称为中央目录)决定实际使用哪个条目(如果有的话)是可以接受的。

此外,ZIP格式只在中央目录中存储某些信息,而不与条目本身一起存储,这是:

  • 内部和外部属性
  • 不同的或额外的额外字段

这意味着在读取不可搜索流时不能真正正确解析ZIP格式,这就是ZipArchiveInputStream被迫做的事情。结果是ZipArchiveInputStream

  • 可能返回根本不属于中心目录的条目,并且不应将其视为存档的一部分。
  • 可以返回多个具有相同名称的条目。
  • 不会返回内部或外部属性。
  • 可能返回不完整的额外字段数据。
  • 如果归档使用数据描述符特性(见下文),可能会返回未知的条目大小和CRC值,直到到达下一个条目。
  • 不能跳过在实际zip流之前出现的字节

整体来看,对于zip压缩包,我们首选ZipFile压缩。

3、解决方法

使用java.util.zip.ZipInputStream类,该类和java.util.zip.ZipFile同属一个工具下,都没问题,因为我传入的是Multipart file,所以只能是ZipInputStream类。

两者对比:

第一种方法使用ZipInputStream类以流的方式逐个读取zip文件中的项,可以在处理每个zip项时选择性地读取和处理其中的内容,适用于较大的zip文件。第二种方法使用ZipFile类直接获取zip文件中的项和对应的输入流,适用于较小的zip文件。根据具体需求和项目情况,可以选择适合的方法来读取zip文件

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
 
public class ZipReader {
    public static void main(String[] args) {
        try (InputStream inputStream = new FileInputStream("example.zip");
             ZipInputStream zipInputStream = new ZipInputStream(inputStream);) {
            ZipEntry entry;
          ZipInputStream zipInputStream = new ZipInputStream(inputStream);) 
            while ((entry = zipInputStream.getNextEntry()) != null) {
                // 处理每个zip文件中的项
                System.out.println("Entry Name: " + entry.getName());
                // 读取zip文件中的内容
                byte[] buffer = new byte[1024];
                int length;
                while ((length = zipInputStream.read(buffer)) > 0) {
                    // 处理每个读取的内容
                    // ...
                }
                zipOutPutStream.putNextEntry(entry);
                // 关闭当前项
                zipInputStream.closeEntry();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

参考链接:

java 读取zip文件的两种方式_zipinputstream 读取文件内容-CSDN博客

ZipException:invalid entry compressed size (expected 24709 but got 24714 bytes)_cause: invalid entry compressed size (expected 174-CSDN博客