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

问题描述 投票:0回答:2

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

ZipInputStream zis = new ZipInputStream(getContentResolver().openInputStream(zipUri));

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

ZipEntry entry = zis.getNextEntry()

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

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

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

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

java android zip uri
2个回答
22
投票

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

不要像

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

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

ZIP 档案知道一个称为数据描述符的功能,这是一种方法 在条目数据之后存储条目的长度。这只能工作 如果可以从中央获取尺寸信息,则可靠 目录或数据本身可以表明它已完成,这是事实 对于使用 DEFLATED 压缩算法压缩的数据。

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

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

解决方案

避免此问题的唯一可能是使用

ZipFile
,但是 JRE 的
ZipFile
实现需要真实文件,因此您可能必须将数据保存到临时文件中。

或者,如果您使用 Apache commons-compress 中的

ZipFile
并且您已经将 ZIP 文件完全保存在内存中,则可以使用
ZipFile.Builder().setByteArray(byteArray).get()
来避免将其保存到临时文件中。


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

ByteArrayOutputStream().use { byteArrayOutputStream ->
    inputStream.copyTo(byteArrayOutputStream)
    ZipFile.Buider().setByteArray(byteArrayOutputStream.toByteArray()).get().use {
        for (entry in it.entries) {
            it.getInputStream(entry).copyTo(someOutputStream)
        }
    }
}

0
投票

尝试使用 commons-compress

<dependency>
   <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.17</version>
</dependency>

这是一个片段

public void unzip(File zipFile, File destDir) throws IOException, ArchiveException {
       String destDirectory = destDir.getAbsolutePath();

       try (ArchiveInputStream i = new ZipArchiveInputStream(new 
          FileInputStream(zipFile), "UTF-8", false, true)) {
          ArchiveEntry entry = null;
          while ((entry = i.getNextEntry()) != null) {
             if (!i.canReadEntryData(entry)) {
                System.out.println("Can't read entry: " + entry);
                continue;
             }
             String name = destDirectory + File.separator + entry.getName();
             File f = new File(name);
             if (entry.isDirectory()) {
                if (!f.isDirectory() && !f.mkdirs()) {
                   throw new IOException("failed to create directory " + f);
                }
             } else {
                File parent = f.getParentFile();
                if (!parent.isDirectory() && !parent.mkdirs()) {
                   throw new IOException("failed to create directory " + parent);
                }
                try (OutputStream o = Files.newOutputStream(f.toPath())) {
                   IOUtils.copy(i, o);
                }
             }
          }
       }
    }
© www.soinside.com 2019 - 2024. All rights reserved.