How does ZipInputStream.getNextEntry() work? - java

Say we have code like:
File file = new File("zip1.zip");
ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
Let's assume you have a .zip file that contains the following:
zip1.zip
hello.c
world.java
folder1
foo.c
bar.java
foobar.c
How would zis.getNextEntry() iterate through that?
Would it return hello.c, world.java, folder1, foobar.c and completely ignore the files in folder1?
Or would it return hello.c, world.java, folder1, foo.c, bar.java, and then foobar.c?
Would it even return folder1 since it's technically a folder and not a file?
Thanks!

Well... Lets see:
ZipInputStream zis = new ZipInputStream(new FileInputStream("C:\\New Folder.zip"));
try
{
ZipEntry temp = null;
while ( (temp = zis.getNextEntry()) != null )
{
System.out.println( temp.getName());
}
}
Output:
New Folder/
New Folder/folder1/
New Folder/folder1/bar.java
New Folder/folder1/foo.c
New Folder/foobar.c
New Folder/hello.c
New Folder/world.java

Yes. It will print the folder name too, since it's also an entry within the zip. It will also print in the same order as it is displayed inside the zip. You can use below test to verify your output.
public class TestZipOrder {
#Test
public void testZipOrder() throws Exception {
File file = new File("/Project/test.zip");
ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
ZipEntry entry = null;
while ( (entry = zis.getNextEntry()) != null ) {
System.out.println( entry.getName());
}
}
}

Excerpt from: https://blogs.oracle.com/CoreJavaTechTips/entry/creating_zip_and_jar_files
java.util.zip libraries offer some level of control for the added entries of the ZipOutputStream.
First, the order you add entries to the ZipOutputStream is the order they are physically located in the .zip file.
You can manipulate the enumeration of entries returned back by the entries() method of ZipFile to produce a list in alphabetical or size order, but the entries are still stored in the order they were written to the output stream.
So I would believe that you have to use the entries() method to see the order in which it will be iterated through.
ZipFile zf = new ZipFile("your file path with file name");
for (Enumeration<? extends ZipEntry> e = zf.entries();
e.hasMoreElements();) {
System.out.println(e.nextElement().getName());
}

The zip file internal directory is a "flat" list of all the files and directories in the zip. getNextEntry will iterate through the list and sequentially identify every file and directory in the zip file.
There is a variant of the zip file format that has no central directory, in which case (if it's handled at all) I suspect you'd iterate through all actual files in the zip, skipping directories (but not skipping files in directories).

Related

How to read a file from a path containing two or more zipfiles in between?

Assume I have a path:
/path/to/zipfile.zip/my/earfile.ear/plainfile
where zipfile.zip is a zip file, and earfile.ear is a ear file. The presence of those compressed files are arbitrary.
I want to have a method which takes this kind of path as param and return an input stream or reader for the 'plainfile'.
I heard that it is easier to implement in Java 7 but I don't know how.
Thank you in advance.
I have come up with a way to solve this problem with restricted condition, which is that there are exactly two zip files are allowed.
public BufferedReader testNestedZipEntry() {
try {
// first zip file wrapped by ZipFile, which allows random access(just feed a path!) to find an entry
ZipFile zipFile = new ZipFile("/path/to/zipfile.zip");
// second zip file wrapped by ZipInputStream, which is sequential so you have to iterate over entries to find the one you need
ZipEntry jarZipEntry = zipFile.getEntry("earfile.ear");
ZipInputStream jarZipIn = new ZipInputStream(zipFile.getInputStream(jarZipEntry));
ZipEntry curEntry = null;
while( (curEntry=jarZipIn.getNextEntry()) != null){
// getNextEntry() method "positions" the input stream to curEntry
// this is the reason why we can achieve this without extracting the file out
if(curEntry.getName().equals("plainfile")) {
return new BufferedReader(new InputStreamReader(jarZipIn));
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
Tip for beginners: Entries in a zip file is all the files you can reach in the whole zip file, not only the ones in the root directory of the zip file.

Why is my file doesn't read to a zip archive

I have a code snippet, that in theory should read a path to an archive first, when write a file to the archive. (But that thing takes zip and for instance some txt file, and really move it to zip, but the file is empty) First, i thought this thing doesn't work since i didn't close streams, but now i use try-with, so the problem should be gone, but it is not.
public void createZip(Path source) throws Exception
{
try(ZipInputStream zipIn = new ZipInputStream(Files.newInputStream(source));
ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile)))
{
ZipEntry zipEntry = new ZipEntry(source.getFileName().toString());
zipOut.putNextEntry(zipEntry);
int data;
while((data = zipIn.read()) > 0)
{
zipOut.write(data);
}
}
}
To use correctly the ZipOutputStream after adding the entry you need to flush the zip.
Try adding the following at the endo of your method:
zipOut.flush();

getNextEntry() doesn't display folder as an entry?

Hi I'm new to android programming.
I'm trying to create a program to unzip a zipped file in my sd card and I noticed something when I debug.
public void testZipOrder() throws Exception {
File file = new File(_zipFile);
zis = new ZipInputStream(new FileInputStream(file));
ZipEntry entry = null;
while ( (entry = zis.getNextEntry()) != null ) {
System.out.println( entry.getName());
}
}
}
this give me an output of :
06-27 00:42:06.360: I/System.out(15402): weee.txt
06-27 00:42:06.360: I/System.out(15402): hi/bye.txt
06-27 00:42:06.360: I/System.out(15402): hi/hiwayne.txt
isn't it suppose to give
weee.txt
hi/
hi/bye.txt
hi/hiwayne.txt
or something that displays its folder instead?
I tried this on my own environment using a test zip file created with 7zip and the following method:
public void testZipOrder() throws Exception {
File file = new File("zip.zip");
ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
ZipEntry entry = null;
while ( (entry = zis.getNextEntry()) != null ) {
System.out.println( entry.getName());
}
zis.close();
}
Note this method is effectively identical to yours.
The resulting output was:
file1.txt
folder1/
folder1/file2.txt
folder1/folder2/
folder1/folder2/file3.txt
Which is, I believe, what you are looking for. As such I expect the problem is with the zip file itself, not your code. It is likely that your zip file does not contain an entry for the directory "hi/".
See here for a basic description of how zip files are structured.
ZIP spec does not require the ordered "placement" of the file and its parent(s) directory in the zip file, and in fact the parent directory entries can be totally absent
See https://bugs.openjdk.java.net/browse/JDK-8054027

How do I enumerate the content of a zipped folder in Java?

ZipeFile file = new ZipFile(filename);
ZipEntry folder = this.file.getEntry("some/path/in/zip/");
if (folder == null || !folder.isDirectory())
throw new Exception();
// now, how do I enumerate the contents of the zipped folder?
It doesn't look like there's a way to enumerate ZipEntry under a certain directory.
You'd have to go through all ZipFile.entries() and filter the ones you want based on the ZipEntry.getName() and see if it String.startsWith(String prefix).
String specificPath = "some/path/in/zip/";
ZipFile zipFile = new ZipFile(file);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry ze = entries.nextElement();
if (ze.getName().startsWith(specificPath)) {
System.out.println(ze);
}
}
You don't - at least, not directly. ZIP files are not actually hierarchical. Enumerate all the entries (via ZipFile.entries() or ZipInputStream.getNextEntry()) and determine which are within the folder you want by examining the name.
Can you just use entries()? API link

java.util.zip.ZipException: too many entries in ZIP file

I am trying to write a Java class to extract a large zip file containing ~74000 XML files. I get the following exception when attempting to unzip it utilizing the java zip library:
java.util.zip.ZipException: too many entries in ZIP file
Unfortunately due to requirements of the project I can not get the zip broken down before it gets to me, and the unzipping process has to be automated (no manual steps). Is there any way to get around this limitation utilizing java.util.zip or with some 3rd party Java zip library?
Thanks.
Using ZipInputStream instead of ZipFile should probably do it.
Using apache IOUtils:
FileInputStream fin = new FileInputStream(zip);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
FileOutputStream fout = new FileOutputStream(new File(
outputDirectory, ze.getName()));
IOUtils.copy(zin, fout);
IOUtils.closeQuietly(fout);
zin.closeEntry();
}
IOUtils.closeQuietly(zin);
The Zip standard supports a max of 65536 entries in a file.
Unless the Java library supports ZIP64 extensions, it won't work properly if you are trying to read or write an archive with 74,000 entries.
I reworked the method to deal with directory structures more convenient and to zip a whole bunch of targets at once.
Plain files will be added to the root of the zip file, if you pass a directory, the underlying structure will be preserved.
def zip (String zipFile, String [] filesToZip){
def result = new ZipOutputStream(new FileOutputStream(zipFile))
result.withStream { zipOutStream ->
filesToZip.each {fileToZip ->
ftz = new File(fileToZip)
if(ftz.isDirectory()){
pathlength = new File(ftz.absolutePath).parentFile.absolutePath.size()
ftz.eachFileRecurse {f ->
if(!f.isDirectory()) writeZipEntry(f, zipOutStream, f.absolutePath[pathlength..-1])
}
}
else writeZipEntry(ftz, zipOutStream, '')
}
}
}
def writeZipEntry(File plainFile, ZipOutputStream zipOutStream, String path) {
zipOutStream.putNextEntry(new ZipEntry(path+plainFile.name))
new FileInputStream(plainFile).withStream { inStream ->
def buffer = new byte[1024]
def count
while((count = inStream.read(buffer, 0, 1024)) != -1)
zipOutStream.write(buffer)
}
zipOutStream.closeEntry()
}

Categories

Resources