Closing ZipOutputStream
Asked Answered
M

5

11

I'm a bit confused. I know that an empty zip is not legal. But what about this sample snippet:

ZipOutputStream zos = null; 
try
{
    zos = new ZipOutputStream(new FileOutputStream("..."));
    //
    //..
    //
}
finally
{
    zos.close();
}

If no zip entries had been added for some reason (possibly exceptional situation) then the following exception will be thrown on close attempt:

Exception in thread "main" java.util.zip.ZipException: ZIP file must have at least one entry
    at java.util.zip.ZipOutputStream.finish(ZipOutputStream.java:304)
    at java.util.zip.DeflaterOutputStream.close(DeflaterOutputStream.java:146)
    at java.util.zip.ZipOutputStream.close(ZipOutputStream.java:321)

In this situation what would be the cleanest way to close the stream?

Thanks...

Mother answered 13/1, 2011 at 15:1 Comment(0)
T
8

You should close the FileOutputStream, not the ZipOutputStream, because the former is what actually consumes system resources.

File zipFile = new File("/tmp/example.zip");
FileOutputStream fos = null;
try
{
   fos = new FileOutputStream(zipFile);
   ZipOutputStream zos = new ZipOutputStream(fos);

   // ...

   zos.close();
}
catch (IOException ex)
{
   // log/report exception, then delete the invalid file
   IOUtils.closeQuietly(fos);
   zipFile.delete();
}
finally
{
   IOUtils.closeQuietly(fos);
}

The IOUtils class is found in Jakarta Commons IO. Using it means that you don't have to deal with the possible-but-rarely-useful IOException that can be thrown by close().

Tatianna answered 13/1, 2011 at 15:14 Comment(3)
Thanks for your help, this should be the truth :-)Mother
This solution is fine, but the reasoning is incorrect: You can close either stream. Closing a wrapper stream like ZipOutputStream will also close the lower-level FileOutputStream.Florance
Notice that Java 7 has introduced a try-with-resource block which makes this kind of code much cleaner!!Bathurst
B
4

You should track if you added stuff the zip stream and close it only when things were added:

ZipOutputStream zos = null; 
OutputStream file = new FileOutputStream("...")
int itemsAdded=0;
try
{
    zos = new ZipOutputStream(file);
    //
    //..
    // itemsAdded++; 
}
finally
{
    if ( itemsAdded > 0 ) {
         zos.close();
    } else {
         file.close();
    }
}

of if you don't need the count just use a boolean flag.

Barely answered 13/1, 2011 at 15:8 Comment(2)
The zip file remains open and locked that way... How to close it?Mother
@lucho added some code to close the file when the zip stream is empty. take a look.Barely
S
4

Instead of closing the stream only when things are added, what I did is a condition check to see if there was anything to zip, before running the zip code. This helped me to simplify the process and I think can be used in general to handle the "ZIP file must have at least one entry" problem. True that closing zos may throw other exceptions, but that is rare.

I think it is problem with Java, that doesn't handle the case when there are no files to zip.

i.e:

int itemsToAdd=0;
//....

if ( itemsToAdd > 0 ) {

    ZipOutputStream zos = new ZipOutputStream(file);
    try {
        //add files to zip
    }
    finally {
        zos.close();
    }
}
Scenarist answered 17/5, 2012 at 3:13 Comment(0)
H
0

If you use Java 7 or above, you can use try-with-resources for better readability

For example:

try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("..."))) {
    // do something with zos
}
Hamiltonian answered 2/6, 2020 at 8:5 Comment(0)
O
0

Using Java 7 or above, just try-with-resource would be enough to close ZipOutputStream, and there is no need to call close() or closeEntry() methods to flush the buffer data and close the stream.

Checked the code of ZipOutputStream::close(), it would call finish(), and inside the finish method, there is a check as following, which means that the closeEntry would be called automatically when the stream closed.

public void finish() throws IOException {
ensureOpen();
if (finished) {
    return;
}
if (current != null) {
    closeEntry();
}
// write central directory
long off = written;
for (XEntry xentry : xentries)
    writeCEN(xentry);
writeEND(off, written - off);
finished = true;

}

Osana answered 8/9, 2023 at 3:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.