6

I currently have a plausible resource leak in my library due to the fact that I hold a ZipFile open so that the returned InputStream for a certain ZipEntry is not closed. However, closing the returned InputStream doesn't close the rest of the ZipFile, so I'm stuck with it left open. Is there a way to safely close the ZipFile and keep the InputStream for returning?

Octavia Togami
  • 4,186
  • 4
  • 31
  • 49

1 Answers1

6

Here's the implementation of InputStream from ZipFile:

/*
* Inner class implementing the input stream used to read a
* (possibly compressed) zip file entry.
*/
private class ZipFileInputStream extends InputStream {

   ...

   public int read(byte b[], int off, int len) throws IOException {
       if (rem == 0) {
           return -1;
       }
       if (len <= 0) {
           return 0;
       }
       if (len > rem) {
           len = (int) rem;
       }
       synchronized (ZipFile.this) {
           ensureOpenOrZipException();

Notice the call to #ensureOpenOrZipException.

So the answer to your question is unfortunately no, there is no way to keep the stream open.

What you could do instead is wrap and hook the #close on the InputStream to close your zip file:

InputStream zipInputStream = ...
return new InputStream() {
    @Override
    public int read() throws IOException {
        return zipInputStream.read();
    }
    @Override
    public void close() throws IOException {
        zipInputStream.close();
        zipFile.close();
    }
}

Another approach would be to buffer it:

InputStream myZipInputStream = ...
//Read the zip input stream fully into memory
byte[] buffer = ByteStreams.toByteArray(zipInputStream);
zipFile.close();
return new ByteArrayInputStream(buffer);

Obviously this has now all gone into memory, so your data will need to be a reasonable size.

Kong
  • 8,792
  • 15
  • 68
  • 98
  • Why's it a hack? It's a simple encapsulation of the zip #close. I added another approach that was my first thought, but it uses a memory buffer, which might not work for you. – Kong Jun 17 '14 at 23:41
  • Actually looking at the code, I realized that `getInputStream` returns just an InputStream, I thought it was more specific. It's not a hack then! – Octavia Togami Jun 18 '14 at 01:13
  • Polymorphism to the rescue! – Kong Jun 18 '14 at 01:40
  • And make sure to wrap your InputSteam with BufferedInputStream. Single byte #read calls perform horribly on the JDK's zip input streams. – Kong Jun 18 '14 at 01:44
  • Since it's a library I can't ensure the wrapping unless I do it on my side, but I think it's safe to assume that most people will convert to `BufferedInputStream`. (Even if they don't, they'd want to be opening a ZipFile and not a regular File). – Octavia Togami Jun 18 '14 at 02:27