ZipInputStream.getNextEntry returns null on some zip files
Asked Answered
H

3

14

I have a simple code to extract zip files, it was working just fine as expected but during my test I tried my code with some zip files (fonts, icons and templates I downloaded from internet) just to make sure it should extract any zip files provided, but its not working with some zip files, here is the minimized code to regenerate this issue:

package com.test.mytest;

import java.io.FileInputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

public class ZipExtractTest {

    public static final String ZIP_FILE = "/Users/XXXXX/Downloads/janne.zip";

    public static void main(String[]args) {
        unzipFile(ZIP_FILE);
        unzipStream(ZIP_FILE);
    }

    public static void unzipFile(String zipName) {
        try {

            ZipFile zf = new ZipFile(zipName);

            Enumeration ent = zf.entries();

            while(ent.hasMoreElements()) {
                System.out.println(ent.nextElement());
            }

        } catch(Exception e) {
            System.out.println(e);
        }
    }

    public static void unzipStream(String zipName) {
        try {
            ZipInputStream zis = new ZipInputStream(new FileInputStream(zipName));
            ZipEntry ze = zis.getNextEntry();

            if(ze == null) {
                System.out.println("unable to get first entry from zip file");
                zis.close();
                return;
            }

            while(ze != null) {
                System.out.println("Entry Found: " + ze);
                ze = zis.getNextEntry();
            }

            zis.closeEntry();
            zis.close();

        } catch(Exception e) {
            System.out.println(e);
        }
    }
}

actually In my real application i have to extract zip files through inputstreams. In the code above I am trying to extract "janne.zip" I downloaded this file from http://www.iconian.com/fonts/janne.zip I am able to extract it using any zip-tool and surprisingly through "unzipFile(String zipName)" method as well, but with unzipStream(String zipName) method

ZipEntry ze = zis.getNextEntry();

returns null

any help would be appreciated

Halfcock answered 20/3, 2013 at 11:14 Comment(3)
Huh. That is weird. +1 for the SSCCE and link to example file.Phenanthrene
Close the ZipFile in your unzipFile method -> http://docs.oracle.com/javase/1.5.0/docs/api/java/util/zip/ZipFile.html#close%28%29Youngstown
@da_re this is just a demo code to regenerate the null issue and close will put no impact on the problem i tried to point out hereHalfcock
M
24

Not an answer as to why this particular file doesn't work with java.util.zip, but if you have the option to replace your use of java.util.zip.ZipInputStream with the Apache commons-compress org.apache.commons.compress.archivers.zip.ZipArchiveInputStream (which should be API-compatible) then I've just tested that on your example file and it seems to work successfully.

Generally I find commons-compress to be much more reliable than java.util.zip at unpacking files created by tools other than the java.util.zip classes themselves.

Edit: I've done a bit of debugging in Eclipse and it looks like this particular zip file has a single segment spanning marker of 0x30304b50 before the LOC signature (0x04034b50) of the first entry's local header. This is something that commons-compress knows how to handle but java.util.zip doesn't - if j.u.z.ZipInputStream sees anything other than a LOC signature then getNextEntry() will return null.

Marthamarthe answered 20/3, 2013 at 11:48 Comment(2)
Thanks Roberts, that's weird. I think I would go with your suggestion and definitely would try commons-compress API as I am looking for some reliable decompression API. Thanks Again!Halfcock
Great that you came back to answer the original question, +1 Just saved me a few hours of searching.Simms
C
5

Funny!

I debugged your code and got the same error. I found an header check in the ZipInputStream implementation, but not in the ZipFile implementation.

Dont ask me why, but the header in your zip file is not valid!

Your file is starting with: 50 4B 30 30 50 4B 03 04
A valid Zip File Header is: 50 4B 03 04

If you delete the first bytes (50 4B 30 30) from your file you got a valid header an you can read you file!

Curator answered 20/3, 2013 at 13:6 Comment(1)
Indeed its Funny! and the problem is I have to deliver this implementation to someone who don't know things like File Headers at all. He is like if i am able to extract a zip file using standard tools then why not through your code. Anyways Thanks - I am able extract it through Apache commons-compress even with such headersHalfcock
C
2


I was having the same problem ! Lucky for me I was able to resolve it.
first i reset the blob data in the database then used java code to zip it using ZipInputStream. Although I am not sure, the null ZipEntry problem could be because of 2 things:
1. The blob data in the database is not stored correctly (or may be its already compressed, some databases compress blob data at the time of storage. you can google this too).
2. The input/output streams can also cause trouble, see this


Here is detailed description of what I did:
1. reset the blob field in database using EMPTY_BLOB and commit changes
2. used the below java program to update the blob field with a .xls file

DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver ()); // register driver

Connection conn =
   DriverManager.getConnection ("jdbc:oracle:thin:@my-local-database:1521:test", "test1", "test1");

// It's faster when auto commit is off: 
conn.setAutoCommit (false);

try
{
      PreparedStatement pstmt = conn.prepareStatement("update content set file_content = ? where CONTENT_ID=2006");
      File blob = new File("C:/Users/ankur/Desktop/Book1.xls");
      FileInputStream in = new FileInputStream(blob);

      pstmt.setBinaryStream(1, in); 
      pstmt.executeUpdate();
      conn.commit();
      conn.close();
      System.out.println("file updated");
}
catch (SQLException e)
{
   e.printStackTrace();
}

Please note that the above code will work but it absolutely does not demonstrate coding standards and practices.
3. Used the below zip method to compress data

public byte[] zipByteArray(String primaryKey, byte[] input) throws IOException{
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ZipOutputStream zos = new ZipOutputStream(baos);
    ZipEntry entry = new ZipEntry(primaryKey);
    entry.setSize(input.length);
    zos.putNextEntry(entry);
    zos.write(input);
    zos.closeEntry();
    zos.close();
    return baos.toByteArray();
}

The above method takes a byte array, zips it, puts it into a ByteArrayOutputStream. You can choose to use the ByteArrayOutputStream itself, due to some requirements I am converting it to byte array.
4. I then insert the above byte array in blob field using prepared statement
5. If I use the unzip code given below, it works fine!

public byte[] unzipInputStream(InputStream is) throws IOException {
    ByteArrayOutputStream byteArrayOutputStream = null;
    ZipInputStream zipIs = new ZipInputStream(new BufferedInputStream(is));
    byteArrayOutputStream = new ByteArrayOutputStream();
    ZipEntry entry = zipIs.getNextEntry();
    while (entry != null) {
        byte[] tmp = new byte[2048];
        BufferedOutputStream bos = null;
        bos = new BufferedOutputStream(byteArrayOutputStream);
        int size = 0;
        while ((size = zipIs.read(tmp)) != -1) {
            bos.write(tmp, 0, size);
        }
        bos.flush();
        bos.close();
        entry = zipIs.getNextEntry();
    }
    zipIs.close();
    return byteArrayOutputStream.toByteArray();

The output of the above method is the unzipped data.

Cusp answered 13/5, 2016 at 6:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.