How to check if a generated zip file is corrupted?
Asked Answered
M

9

28

we have a piece of code which generates a zip file on our system. Everything is ok, but sometimes this zip file while opened by FilZip or WinZip is considered to be corrupted.

So here is my question: how can we check programatically if a generated zip file is corrupted?

Here is the code we are using to generate our zip files:

try {
    ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmpFile));
    byte[] buffer = new byte[16384];
    int contador = -1;
    for (DigitalFile digitalFile : document.getDigitalFiles().getContent()) {
       ZipEntry entry = new ZipEntry(digitalFile.getName());
       FileInputStream fis = new FileInputStream(digitalFile.getFile());
       try {
          zos.putNextEntry(entry);
          while ((counter = fis.read(buffer)) != -1) {
             zos.write(buffer, 0, counter);
          }
          fis.close();
          zos.closeEntry();
       } catch (IOException ex) {
          throw new OurException("It was not possible to read this file " + arquivo.getId());
       }
    }
    try {
      zos.close();
    } catch (IOException ex) {
      throw new OurException("We couldn't close this stream", ex);
    }

Is there anything we are doing wrong here?

EDIT: Actually, the code above is absolutely ok. My problem was that I was redirecting the WRONG stream for my users. So, instead of opening a zip file they where opening something completely different. Mea culpa :(

BUT the main question remains: how programatically I can verify if a given zip file is not corrupted?

Maturation answered 18/1, 2010 at 11:30 Comment(1)
Just one additional issue: zos won't be closed if an exception is thrown. Could you paste the rest of the outer try/catch/finally?Gait
M
35

You can use the ZipFile class to check your file :

 static boolean isValid(final File file) {
    ZipFile zipfile = null;
    try {
        zipfile = new ZipFile(file);
        return true;
    } catch (IOException e) {
        return false;
    } finally {
        try {
            if (zipfile != null) {
                zipfile.close();
                zipfile = null;
            }
        } catch (IOException e) {
        }
    }
}
Mistymisunderstand answered 18/1, 2010 at 14:52 Comment(5)
Perfect! Didn't know this class. Thank's a lot!Maturation
If you want to test all the zip file (not only check if it has zip heads) then do a complete read of it using zip file (iterate over its entries and for each one ask for the stream and read it until it ends).Immoderacy
The zip file format is redundant, so you can't for certain make sure that it is not corrupt. Best you can do without having your own implementation is read all the data through ZipFile and through ZipInputStream and compare secure hashes.Thrasher
ZipException is an IOException. The extra catch block is therefore redundant.Surgeonfish
Can we do this same to VideoFile?Marika
D
12

I know its been a while that this has been posted, I have used the code that all of you provided and came up with this. This is working great for the actual question. Checking if the zip file is corrupted or not

private boolean isValid(File file) {
    ZipFile zipfile = null;
    ZipInputStream zis = null;
    try {
        zipfile = new ZipFile(file);
        zis = new ZipInputStream(new FileInputStream(file));
        ZipEntry ze = zis.getNextEntry();
        if(ze == null) {
            return false;
        }
        while(ze != null) {
            // if it throws an exception fetching any of the following then we know the file is corrupted.
            zipfile.getInputStream(ze);
            ze.getCrc();
            ze.getCompressedSize();
            ze.getName();
            ze = zis.getNextEntry();
        } 
        return true;
    } catch (ZipException e) {
        return false;
    } catch (IOException e) {
        return false;
    } finally {
        try {
            if (zipfile != null) {
                zipfile.close();
                zipfile = null;
            }
        } catch (IOException e) {
            return false;
        } try {
            if (zis != null) {
                zis.close();
                zis = null;
            }
        } catch (IOException e) {
            return false;
        }

    }
}
Ddene answered 20/6, 2013 at 20:3 Comment(2)
Thanks for this! One suggestion, you could simplify this a bit by using ZipFile.entries() to get each ZipEntry, saving the hassle of creating (and closing) the ZipInputStream.Siamang
ze.get*() doesn't do anything. You can see here: grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…Reisch
D
3

I think you'll see correspondent exception stack trace during zip-file generation. So, you probably wan't to enhance your exception handling.

Dol answered 18/1, 2010 at 11:36 Comment(1)
Actually no exception is thrown in the process. The zip file is fully generated, but we can't open it with Filzip, Winzip or any other zip extraction application. :/Maturation
C
2

in my implementation it looks like that. maybe it helps you:

//[...]

try {
    FileInputStream fis = new FileInputStream(file);
    BufferedInputStream bis = new BufferedInputStream(fis);

    zos.putNextEntry(new ZipEntry(file.getName()));

    try {
        final byte[] buf = new byte[BUFFER_SIZE];
        while (true) {
            final int len = bis.read(buf);
            if (len == -1) {
                break;
            }
            zos.write(buf, 0, len);
        }
        zos.flush();
        zos.closeEntry();
    } finally {
        try {
            bis.close();
        } catch (IOException e) {
            LOG.debug("Buffered Stream closing failed");
        } finally {
            fis.close();
        }
    }
} catch (IOException e) {
    throw new Exception(e);
}

//[...]
zos.close
Clutch answered 20/1, 2011 at 15:56 Comment(0)
B
1

Perhaps swap the following two lines?;

fis.close();
zos.closeEntry();

I can imagine that the closeEntry() will still read some data from the stream.

Bugger answered 18/1, 2010 at 11:43 Comment(0)
S
1

Your code is basically OK, try to find out which file is responsible for the corrupted zip file. Check whether digitalFile.getFile() always returns a valid and accessible argument to FileInputStream. Just add a bit logging to your code and you will find out what's wrong.

Sinclare answered 18/1, 2010 at 13:6 Comment(0)
H
1
new ZipFile(file) 

compress again the file, so duplicate efforts and that is not what you are looking for. Despite of the fact that only check one file and the question compress n-files.

Take a look to this: http://www.kodejava.org/examples/336.html

Create a checksum for your zip:

CheckedOutputStream checksum = new CheckedOutputStream(fos, new CRC32());
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(checksum));
...

And when you finish the compression show it

System.out.println("Checksum   : " + checksum.getChecksum().getValue());

You must do the same reading the zip with java or others tools checking if checksums match.

see https://mcmap.net/q/503670/-checksums-on-zip-files for more information

Halvaard answered 27/9, 2012 at 1:27 Comment(0)
J
0

ZipOutputStream does not close the underlying stream.

What you need to do is:

FileOutputStream fos = new FileOutputStream(...);
ZipOutputStream zos = new ZipOutputStream(fos);

Then in your closing block:

zos.close();
fos.flush(); // Can't remember whether this is necessary off the top of my head!
fos.close();
Janik answered 18/1, 2010 at 11:42 Comment(2)
Well: I tried, but the problem persists. Actually, our problem is happening in a single case only. :/Maturation
ZipOutputStream does not close the underlying stream - this seems to be wrong. finish() doesn't close, but close() does. This can be verified in source of DeflaterOutputStream#close() method.Prader
T
0

I simplified the code from Nikhil Das Nomula by using try-with-resources:

    public static boolean isValidZipFile(File file) {
        try(ZipFile zipfile = new ZipFile(file); 
            ZipInputStream zis = new ZipInputStream(new FileInputStream(file))
        ) {
            ZipEntry ze = zis.getNextEntry();
            if (ze == null) {
                return false;
            }
            while (ze != null) {
                // if it throws an exception fetching any of the following then we know the file is corrupted.
                zipfile.getInputStream(ze);
                ze.getCrc();
                ze.getCompressedSize();
                ze.getName();

                ze = zis.getNextEntry();
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

At first I thought using ZipFile was redundant, but then I deleted one byte at the end of the archive file. ZipEntry did not show the problem, but ZipFile threw the error java.util.zip.ZipException: zip END header not found.

Teeth answered 23/8, 2023 at 12:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.