WAI
Status Update
Comments
ad...@google.com <ad...@google.com>
ad...@google.com <ad...@google.com> #2
We have passed this to the development team and will update this issue with more information as it becomes available.
ad...@google.com <ad...@google.com> #3
The behavior is consistent with previous Android versions (all the way back to Android 1.0). Reads on a ZipInputStream will fail for STORed entries that have a data descriptor suffix.
This is not consistent with ZipFile but it's impossible to fix for a ZipInputStream (since we can't really know anything about the CD). However, it should be very rare -- zip tools have no legitimate reason to append a data descriptor for STORed entries and the commonly used zip tools that ship with Linux / Mac OS etc. don't do this. It would be interesting to know what tools the developer used to produce this zip file, as it's highly unusual.
There's not much else we can do in the platform. The developer can either :
- Change the tools that generate zip files
- Use a different zip parsing library (e.g apache commons)
This is not consistent with ZipFile but it's impossible to fix for a ZipInputStream (since we can't really know anything about the CD). However, it should be very rare -- zip tools have no legitimate reason to append a data descriptor for STORed entries and the commonly used zip tools that ship with Linux / Mac OS etc. don't do this. It would be interesting to know what tools the developer used to produce this zip file, as it's highly unusual.
There's not much else we can do in the platform. The developer can either :
- Change the tools that generate zip files
- Use a different zip parsing library (e.g apache commons)
lb...@gmail.com <lb...@gmail.com> #4
@3 I'm not the one who create the file. It's from some website (ApkPure).
I have however modified it to have less files inside, using 7-zip app (on Windows OS)
https://www.7-zip.org/download.html
Currently ZipInputStream, including the one of Apache - can't handle it.
For Apache, I can, however, use an all-in-memory solution that acts like ZipFile (example here:https://stackoverflow.com/a/54236244/878126 ) , but sadly this won't work well because the heap size is not dynamic on Android and can easily be too small for the task.
This is important in case you need to handle a Uri or even a zip file within another, while wanting to avoid extracting to real files.
I've created alternative solutions too (here:https://stackoverflow.com/q/61837961/878126 and here: https://stackoverflow.com/q/61652063/878126 ) , but there is nothing as efficient as what the developers of ZipFile can do. Given a Uri or a way that I tell you how to reach every tiny bit of information (example is a zip file inside another zip file), you can have whatever you wish in it, so it doesn't matter which format it is, because you can reach everywhere there anyway.
So, as both of the solutions you've offered aren't possible, please do improve the API to support all kinds of zip files, and let ZipInputStream work well with zip files.
I have however modified it to have less files inside, using 7-zip app (on Windows OS)
Currently ZipInputStream, including the one of Apache - can't handle it.
For Apache, I can, however, use an all-in-memory solution that acts like ZipFile (example here:
This is important in case you need to handle a Uri or even a zip file within another, while wanting to avoid extracting to real files.
I've created alternative solutions too (here:
So, as both of the solutions you've offered aren't possible, please do improve the API to support all kinds of zip files, and let ZipInputStream work well with zip files.
ad...@google.com <ad...@google.com> #5
It's not really feasible to make ZipInputStream work correctly with these files because it needs to know the size of each entry *before* it starts reading it, otherwise there's no way to tell when the entry ends. Reading the central directory is not an option without using arbitrary amounts of RAM. We don't have any plans to change this behaviour.
If you're using Apache common's ZipFile, note that you can construct a ZipFile with a SeekableByteChannel. A FileChannel is a seekable byte channel, so you can obtain one by doing "new FileInputStream(fd).getChannel()".
If you're using Apache common's ZipFile, note that you can construct a ZipFile with a SeekableByteChannel. A FileChannel is a seekable byte channel, so you can obtain one by doing "new FileInputStream(fd).getChannel()".
lb...@gmail.com <lb...@gmail.com> #6
@5 Can you please explain?
As I found, this won't work if the file is inside a zipped file, or available via Uri...
And SeekableByteChannel is only implemented there by SeekableInMemoryByteChannel, which holds the entire file in memory, and that's a problem on Android since the heap is quite limited and can't be enlarged dynamically.
So what I did is this:
https://github.com/AndroidDeveloperLB/ZipFileInMemoryJNI
Still, not as good as using ZipFile...
As I found, this won't work if the file is inside a zipped file, or available via Uri...
And SeekableByteChannel is only implemented there by SeekableInMemoryByteChannel, which holds the entire file in memory, and that's a problem on Android since the heap is quite limited and can't be enlarged dynamically.
So what I did is this:
Still, not as good as using ZipFile...
Description
- Steps to reproduce the problem (including sample code if appropriate).
1. Copy a valid ZIP file as attached here ("test.zip"). It has an image file inside, and you can open it via the PC too, if you wish, or via a file manager app on the device, such as Total Commander.
2. Try to handle the zip file using ZipFile and using ZipInputStream on both Android R and before.
//assumption: storage permission is granted (use "-g" when installing if you wish).
// Also the zip file exists on the given path
thread {
val file = File("/storage/emulated/0/test.zip")
Log.d("AppLog", "opening using ZipFile:")
ZipFile(file).use { zipFile: ZipFile ->
for (entry in zipFile.entries())
Log.d("AppLog", "entry:${
}
Log.d("AppLog", "opening using ZipInputStream:")
ZipInputStream(FileInputStream(file)).use { zipInputStream: ZipInputStream ->
while (true) {
val entry = zipInputStream.nextEntry ?: break
Log.d("AppLog", "entry:${
}
}
}
}
- What happened.
Using ZipFile, it works fine, but only on older Android versions.
Here it won't work, and same goes for ZipInputStream (which is used by SAF)
It won't let you using any of those. Instead it will crash:
java.util.zip.ZipException: only DEFLATED entries can have EXT descriptor
at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:321)
at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:124)
at com.lb.ziptest.MainActivity$onCreate$1.invoke(MainActivity.kt:28)
at com.lb.ziptest.MainActivity$onCreate$1.invoke(MainActivity.kt:12)
at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)
- What you think the correct behavior should be.
Should work fine.
It's just a zip file, and ZipFile could handle it just fine on older Android versions, and now both don't work.
It should also be possible to open zip files using SAF, otherwise it's yet another reason to call it a terrible API.
Note that not always the files from SAF are true files on the storage, so it should be possible to still handle them.