Unzipping with ZipInputStream never finishes

2019-07-10 12:45发布

问题:

I'm using an AsyncTask to unzip a file, and all seems to be going well (all the files in the ZIP archive are extracted), but my unzip method never finishes.

Here's the source for my unzip class:

public class MyUnzipper {


    public static boolean unzipFileIntoDirectory(String inputFilename, String outputPath) throws Exception {
        ZipInputStream zis = null;
        BufferedOutputStream dest = null;

        try {
            File archive = new File(inputFilename);
            File destinationDir = new File(outputPath);

            final int BUFFER_SIZE = 1024;

            zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(archive), BUFFER_SIZE));
            ZipEntry entry = null;
            File destFile;
            while( (entry = zis.getNextEntry()) != null ){

                destFile = new File(destinationDir, entry.getName());

                if( entry.isDirectory() ){
                    destFile.mkdirs();
                }
                else {
                    // check for and create parent directories if they don't exist
                    File parentDir = destFile.getParentFile();
                    if ( null != parentDir ) {
                        if ( !parentDir.isDirectory() ) {
                            parentDir.mkdirs();
                        }
                    }

                    int count;
                    byte data[] = new byte[BUFFER_SIZE];
                    dest = new BufferedOutputStream(new FileOutputStream(destFile), BUFFER_SIZE);
                    Log.i("MyUnzipper", "Beginning unzip of " + destFile);

                    while( (count = zis.read(data, 0, BUFFER_SIZE)) != -1 ){ 
                        dest.write(data, 0, count);
                        Log.v("MyUnzipper", "Count = " + count);
                    }
                    dest.flush();
                    dest.close();
                    dest = null;
                }

                zis.closeEntry();

                Log.wtf("MyUnzipper", "Unzipped entry " + entry.getName());
            }

            Log.wtf("MyUnzipper", "Unzip done");
        }
        catch(Exception e){
            Log.wtf("MyUnzipper", "Unzip error");
            e.printStackTrace();
            return false;
        }
        finally {
            if( null != zis )
                zis.close();

            if( null != dest )
                dest.close();
        }

        return true;
    }

}

When I debug this line-by-line, it runs fine until it has unzipped all files, then it gets to zis.closeEntry(), and the debugger just "goes away", i.e. the next line (Log.wtf(...)) is never executed. My AsyncTask is never finished, it's just as if I'm stuck in an infinite loop?! But looking at the source for ZipInputStream.closeEntry() there doesn't seem to be any loops or anything suspicious there?

I have also tried extracting the ZIP archive using ZipFile instead of ZipInputStream, but then I get the following error:

java.util.zip.ZipException: End Of Central Directory signature not found

There is nothing wrong with the ZIP file, I have tested it with zip -v -T on Mac OSx. I have also tried re-zipping it using ZIP version 3.0 and 2.1 (original was 2.0). I can unzip all version without any problems on Mac OSx (using The Unarchiver and Archive Utility).

This is driving me nuts, what can possibly be wrong?

 

Update (solved)

Turns out to be a really stupid problem, not really related to the unzipping.

I'm downloading the ZIP files from a server before unzipping them, and apparently I forgot to call close() on the output stream from the download operation before starting the unzip operation.

Perhaps this thread can help someone else who makes the same stupid mistake.

回答1:

Well, sometimes your ZIP extracting code can get stuck even if you closes all previous output and input streams. And, this is a known bug: ZipInputStream#read can return 0.

Addition:

If your ZIP file contains some files with non-ACSII file names, you'll face problem with extracting. Android's ZipInputStream doesn't work well with UTF-8, CP437 and so on.

In that case Apache Commons should be a solution:

private boolean unpack(File zipFile, File targetDir) {
    ZipFile zip = null;
    try {
        zip = new ZipFile(zipFile.getAbsoluteFile());
        final Enumeration<ZipArchiveEntry> entries = zip.getEntries();
        while(entries.hasMoreElements()) {
            ZipArchiveEntry entry = entries.nextElement();
            if (entry.isDirectory()) {
                mkdirsOrThrow(new File(targetDir, entry.getName()));
                continue;
            }
            final File entryDestination = new File(targetDir,  entry.getName());
            mkdirsOrThrow(entryDestination.getParentFile());
            final InputStream in = zip.getInputStream(entry);
            final OutputStream out = new FileOutputStream(entryDestination);
            IOUtils.copy(in, out);
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(out);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        if (zip!= null) try {
            zip.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return true;
}