2

I am using commons compress to zip multiple files and send it the client from a Servlet. The files could be a combination of any type of files(text, video, audio, archives, images etc). I take the inputStream of file and write to ServletOutputStream using IOUtils.copy(is, os). The code usually works fine for any document combination but when there is a request to download files that contain more than 1 zip, I get java.io.IOException: Closed As a result, the zip file created is corrupted even though the size of zip is summation of individual filesizes(I am not using compression).

I tried to locally create zip and use FileOutputStream instead of response.getOutputStream() in the constructor of ZipArchiveOutputStream and it succeeds. So, it looks like the problem exists for ServletOutputStream.

Can anyone suggest any workaround.

Here is my code :

`try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream( response.getOutputStream())) {
        //get fileList
        for(File file : files) {
           addFileToZip(zos, file.getName(), new BufferedInputStream(new FileInputStream(file)));
        }
        zos.close()
    }

`

public static void addFileToZip(ZipArchiveOutputStream zipOutputStream, String filename, InputStream inputStream) throws FileNotFoundException {
    if(zipOutputStream != null && inputStream != null) {
        try {
            zipOutputStream.putArchiveEntry(new ZipArchiveEntry(filename));
            IOUtils.copy(inputStream, zipOutputStream);
            logger.debug("fileAddedToZip :" + filename);
        } catch (IOException e) {
            logger.error("Error in adding file :" + filename, e); 
        } finally {
            try {
                inputStream.close();
                zipOutputStream.closeArchiveEntry(); //**Starts to fail here after 1st zip is added**
            } catch (IOException e) {
                logger.error("Error in closing zip entry :" + filename, e);
            }
        }
    }
`

Here is the exception trace : `

java.io.IOException: Closed
        at org.mortbay.jetty.AbstractGenerator$Output.write(AbstractGenerator.java:627)
        at org.mortbay.jetty.AbstractGenerator$Output.write(AbstractGenerator.java:577)
        at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.writeOut(ZipArchiveOutputStream.java:1287)
        at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.writeOut(ZipArchiveOutputStream.java:1272)
        at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.writeDataDescriptor(ZipArchiveOutputStream.java:997)
        at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.closeArchiveEntry(ZipArchiveOutputStream.java:461)
        at xxx.yyy.zzz.util.ZipUtils.addFileToZip(ZipUtils.java:110)

line 110 is zipOutputStream.closeArchiveEntry(); //**Starts to fail here after 1st zip is added**

Thanks in advance.

Raj Saxena
  • 852
  • 10
  • 18
  • What is the inpputStream an why you close it in the addFileToZip() ? – StanislavL Sep 08 '14 at 08:05
  • inputStream is the `FileInputStream` of the zip that I want to add. I am closing it to free its resources once its copied. – Raj Saxena Sep 08 '14 at 09:44
  • Apart from removing the extraneous `zos.close()` you could try `zipOutputStream.flush()` before `closeArchiveEntry()` because I cannot see anything else wrong. – Joop Eggen Sep 08 '14 at 10:23
  • Tried `zipOutputStream.flush()` but it doesn't works. Also, the zip creation is successful for any other file type combination except when the files have more than 1 zip file. – Raj Saxena Sep 08 '14 at 10:44
  • does anyone has a solution here? – Raj Saxena Sep 15 '14 at 04:58

1 Answers1

0

The problem is that you use try-with-resources which automatically closes the stream you create in it, and yet you also close it manually, and therefore when the JVM tries to auto-close it is when you get java.io.IOException: Closed exception because it is already closed.

If you use try-with-resources, you don't need to close the streams you create in it. Remove your manual zos.close() statement:

try (ZipArchiveOutputStream zos =
         new ZipArchiveOutputStream(response.getOutputStream())) {

    //get fileList
    for(File file : files) {
       addFileToZip(zos, attachment.getFileName(), is);
    }
} // Here zos will be closed automatically!

Also note that once zos is closed, it will also close the servlet's underlying OutputStream so you will not be able to add further entries. You have to add all before it is closed.

icza
  • 389,944
  • 63
  • 907
  • 827
  • I edited my code to show the correct location of closing parenthesis for the try. As you can see, the automatic closing of `zos` would only happen when I am done adding all files. But, the problem here is that any subsequent zip addition after 1st starts failing(look for my comment in 2nd code snippet). – Raj Saxena Sep 08 '14 at 09:42
  • Do you properly create the `is` input stream which you pass to your `addFileToZip()` method? Please post all your code. – icza Sep 08 '14 at 09:49
  • Edited the code. @icza the problem here is not how I get the stream but rather why the code works for **all** other cases and file combinations except those containing more than 1 zip. Why does the call `zipOutputStream.closeArchiveEntry()` starts to fail after initial entry & how can I make it work for more than 1 zip? – Raj Saxena Sep 08 '14 at 10:10
  • Please post exception stacktrace and indicate which line it is in your code. – icza Sep 08 '14 at 10:24
  • After the `try` block the `zos` stream and the servlet's output stream is closed. You're not trying to add more entries after that, right? – icza Sep 08 '14 at 12:50
  • No, I am not. Like I said before, it works for all cases except when more than 1 zip are added. – Raj Saxena Sep 09 '14 at 05:03
  • Here is the exception trace :`java.io.IOException: Closed at org.mortbay.jetty.AbstractGenerator$Output.write(AbstractGenerator.java:627) at org.mortbay.jetty.AbstractGenerator$Output.write(AbstractGenerator.java:577) at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.writeOut(ZipArchiveOutputStream.java:1287) at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.writeOut(ZipArchiveOutputStream.java:1272) at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.writeDataDescriptor(ZipArchiveOutputStream.java:997)` – Raj Saxena Sep 18 '14 at 04:41