This isn't a problem with zip files themselves (though it is a horrific format), but the java.util.zip
API, and probably zlib
which it is typically implemented with.
ZipFile
requires a File
which it likes to memory map. If the "file" is actually a nested entry, that's not going to fly unless you copy it out, or have some OS-specific trick up your sleeve.
If the nested zip file is compressed within the outer zip file, random access is obviously out. You would need a different API anyway. However, java.util.zip
does have ZipInputStream
. Don't treat it as an InputStream
- that's a typically strange subtyping arrangement. It does allow you to stream out entries, even if the archive is a compressed entry of the outer file.
(Roughly ZIP files work like this: At the end of the file is a central directory. In order to access the archive in a random access manner, you need to load the end of the file and read that in. It contains names, lengths, etc., as well as an offset to each entry in the file. The entries contain names, lengths, etc., and the actual file contents. No, they needn't be consistent, or have any kind of 1-1 correlation. May also contain other lies, such as the decompressed length being wrong or -1. Anyway, you can ignore the central directory and read the entries sequentially.
JARs add to the fun by adding an INDEX.LST
and a META-INF/manifest.mf
as the first entries of the file. The former contains an index, similar to the central directory, but at the front rather than the end. The latter may contain a listing of the files together with signatures. Executable zips and GIFARs (and I think similar, earlier discovered equivalents for Microsoft products) may have something stuffed in front of the zip, so you have to go in through the rear for those.)
A small demonstration program.
import java.io.*;
import java.util.zip.*;
interface Code {
static void main(String[] args) throws Exception {
ZipFile zipZip = new ZipFile("zip.zip.zip");
ZipEntry zipEntry = zipZip.getEntry("zip.zip");
if (zipEntry == null) {
throw new Error("zip.zip not found");
}
InputStream zipIn = zipZip.getInputStream(zipEntry);
ZipInputStream zip = new ZipInputStream(zipIn);
for (;;) {
ZipEntry entry = zip.getNextEntry();
if (entry == null) {
break;
}
System.err.println(entry.getName());
new BufferedReader(new InputStreamReader(zip)).lines().forEach(l -> {
System.err.println("> "+l);
});
}
}
}