Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ZipInputStream.getNextEntry returns null on some zip files

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

like image 581
spring_dev101 Avatar asked Mar 20 '13 11:03

spring_dev101


3 Answers


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.

like image 124
tewari2312 Avatar answered Oct 18 '22 03:10

tewari2312


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.

like image 35
Ian Roberts Avatar answered Oct 18 '22 05:10

Ian Roberts


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!

like image 5
Mirko Avatar answered Oct 18 '22 03:10

Mirko