Android ZipInputStream:只有 DEFLATED 条目可以有 EXT 描述符

Luc*_*ico 4 java zip android uri

在我的 android 设备上,我需要从内容 uri 中提取一个文件(一个 xapk,据我所知,这是一个普通的 zip 存档)。我正在使用以下代码行创建 ZipInputStream:

ZipInputStream zis = new ZipInputStream(getContentResolver().openInputStream(zipUri));
Run Code Online (Sandbox Code Playgroud)

然后我尝试使用以下命令读取存档的第一个条目:

ZipEntry entry = zis.getNextEntry()
Run Code Online (Sandbox Code Playgroud)

问题是我得到了这个异常:

java.util.zip.ZipException:只有 DEFLATED 条目可以有 EXT 描述符

我 100% 确定存档中没有 0bytes 文件,并且我可以使用设备中的其他实用程序(RAR、解压缩等)提取相同的存档。

如果我使用带有硬编码路径的 ZipFile(因此不涉及内容 uri),我可以毫无问题地提取相同的存档,因此问题与带有 uri 的 ZipInputStream 相关。另一方面,我不能在这里使用 ZipFile,因为它不支持内容 uri。

Rob*_*ert 11

不幸的是,目前唯一的答案是:

不要像ZipInputStream这样以流模式处理 ZIP 文件。似乎所有当前可用的 ZIP 处理组件(例如ZipInputStream来自 JRE 和ZipArchiveInputStream来自Apache commons-compress 的组件)都无法处理此类 ZIP 文件。

apache commons-compress 帮助页面上有很好的问题描述:

ZIP 档案知道一种称为数据描述符的功能,它是一种在条目数据之后存储条目长度的方法。只有当大小信息可以从中央目录中获取或者数据本身可以表示它是完整的时,这才能可靠地工作,这对于使用 DEFLATED 压缩算法压缩的数据来说是正确的。

ZipFile 可以访问中央目录,并且可以可靠地使用数据描述符提取条目。只要条目是 DEFLATED,ZipArchiveInputStream 也是如此。对于 STORED 条目,ZipArchiveInputStream 可以尝试提前读取,直到找到下一个条目,但这种方法不安全,必须通过构造函数参数显式启用。

https://commons.apache.org/proper/commons-compress/zip.html

解决方案

避免此问题的唯一方法是使用ZipFile,但是ZipFileJRE的实现需要真实文件,因此您可能必须将数据保存到临时文件中。

或者,如果您使用ZipFileApache commons-compress替代,并且您已经在内存中完全拥有 ZIP 文件,则可以避免使用SeekableInMemoryByteChannel替代将其保存到临时文件中。


编辑:使用 Apache (Kotlin) 内存 ZipFile 的解决方案:

ByteArrayOutputStream().use { byteArrayOutputStream ->
    inputStream.copyTo(byteArrayOutputStream)
    ZipFile(SeekableInMemoryByteChannel(byteArrayOutputStream.toByteArray())).use {
        for (entry in it.entries) {
            it.getInputStream(entry).copyTo(someOutputStream)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)