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)
1)mimetype的本地文件头中的通用位标志表明它没有数据描述符,实际上有一个。我们没有跳过mimetype的数据描述符。在读取content.xml(某个文件)时,我们实际上是在读取mime类型的数据描述符,因为我们没有事先跳过它,这导致了一个异常。
2)似乎是条形码/邮编的问题?仍然不确定zip归档文件是如何创建的。但在我看来,这不是Commons Compress的问题。这是创建zip文件的库的问题。你可以用ZipFile提取它。这是因为ZipFile和ZipArchiveInputStream在提取zip存档时有不同的机制——ZipFile基于中央文件头,而ZipArchiveInputStream基于本地文件头
2、我的理解
ZipArchiveInputStream和ZipFile两种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();
}
}
}
参考链接: