在我的android设备上,我需要提取一个从内容uri获取的文件(一个xapk,据我所知,这是一个纯zip压缩文件)。我使用下面这行代码创建ZipInputStream:
ZipInputStream zis = new ZipInputStream(getContentResolver().openInputStream(zipUri));
然后,我尝试用以下命令读取归档文件的第一个条目:
ZipEntry entry = zis.getNextEntry()
问题是我得到了这个异常:
java.util.zip.ZipException:只有经过压缩的条目才能具有EXT描述符
我100%确定归档文件中没有0字节的文件,并且我可以使用设备中的其他实用程序(RAR、解压等)提取相同的归档文件。
如果我使用具有硬编码路径的ZipFile (因此不涉及内容uri ),我可以毫无问题地提取相同的归档文件,因此问题与具有uri的ZipInputStream有关。另一方面,我不能在这里使用ZipFile,因为它不支持内容uris。
发布于 2019-01-17 20:43:58
不幸的是,目前唯一的答案是:
不要像ZipInputStream
那样以流模式处理ZIP文件。似乎所有当前可用的压缩处理组件,如JRE的ZipInputStream
和Apache commons-compress的ZipArchiveInputStream
都不能处理这样的压缩文件。
在apache commons-compress帮助页面上有一个非常好的问题描述:
ZIP存档知道一个叫做数据描述符的特性,它是一种在条目数据之后存储条目长度的方法。只有当大小信息可以从中央目录获取,或者数据本身可以发出完成的信号时,这才能可靠地工作,这对于使用压缩压缩算法压缩的数据是正确的。
ZipFile可以访问中央目录,并且可以使用数据描述符可靠地提取条目。只要条目是空的,ZipArchiveInputStream也是如此。对于存储的条目,ZipArchiveInputStream可以尝试预读,直到找到下一个条目,但是这种方法是不安全的,必须通过构造函数参数显式启用。
https://commons.apache.org/proper/commons-compress/zip.html
解决方案
避免这个问题的唯一可能是使用ZipFile
,但是ZipFile
实现需要一个真实的文件,因此您可能必须将数据保存到临时文件中。
或者,如果您使用Apache commons-compress中的ZipFile
,并且您已经将ZIP文件完全存储在内存中,则可以避免使用SeekableInMemoryByteChannel
将其保存到临时文件中。
编辑:使用内存中ZipFile的解决方案(Kotlin):
ByteArrayOutputStream().use { byteArrayOutputStream ->
inputStream.copyTo(byteArrayOutputStream)
ZipFile(SeekableInMemoryByteChannel(byteArrayOutputStream.toByteArray())).use {
for (entry in it.entries) {
it.getInputStream(entry).copyTo(someOutputStream)
}
}
}
发布于 2018-06-16 06:49:14
尝试使用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);
}
}
}
}
}
https://stackoverflow.com/questions/47208272
复制相似问题