views:

5056

answers:

10

I am trying to read a single file from a java.util.zip.ZipInputStream, and copy it into a java.io.ByteArrayOutputStream (so that I can then create a java.io.ByteArrayInputStream and hand that to a 3rd party library that will end up closing the stream, and I don't want my ZipInputStream getting closed).

I'm probably missing something basic here, but I never enter the while loop here:

  ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream();
  int bytesRead;
  byte[] tempBuffer = new byte[8192*2];
  try {
   while ( (bytesRead = zipStream.read(tempBuffer)) != -1 ){
    streamBuilder.write(tempBuffer, 0, bytesRead);
   }
  } catch (IOException e) {
   ...
  }

What am I missing that will allow me to copy the stream? Thanks.

Edit: I should have mentioned earlier that this ZipInputStream is not coming from a file, so I don't think I can use a ZipFile. It is coming from a file uploaded through a servlet.

Also, I have already called getNextEntry() on the ZipInputStream before getting to this snippet of code. If I don't try copying the file into another InputStream (via the OutputStream mentioned above), and just pass the ZipInputStream to my 3rd party library, the library closes the stream, and I can't do anything more, like deal with the remaining files in the stream.

A: 

Check if the input stream is positioned in the begging.

Otherwise, as implementation: I do not think that you need to write to the result stream while you are reading, unless you process this exact stream in another thread.

Just create a byte array, read the input stream, then create the output stream.

Sunny
A: 

It is unclear how you got the zipStream. It should work when you get it like this:

  zipStream = zipFile.getInputStream(zipEntry)
I just added a clarification about this, but it isn't comiing from a file.
pkaeding
+1  A: 

I'd use IOUtils from the commons io project.

IOUtils.copy(zipStream, byteArrayOutputStream);
ScArcher2
This looks like it might work. I will try it when I get to work tomorrow. Thanks.
pkaeding
+2  A: 
bene
A: 

t is unclear how you got the zipStream. It should work when you get it like this:

  zipStream = zipFile.getInputStream(zipEntry)

If you are obtaining the ZipInputStream from a ZipFile you can get one stream for the 3d party library, let it use it, and you obtain another input stream using the code before.

Remember, an inputstream is a cursor. If you have the entire data (like a ZipFile) you can ask for N cursors over it.

A diferent case is if you only have an "GZip" inputstream, only an zipped byte stream. In that case you ByteArrayOutputStream buffer makes all sense.

helios
+1  A: 

I would call getNextEntry() on the ZipInputStream until it is at the entry you want (use ZipEntry.getName() etc.). Calling getNextEntry() will advance the "cursor" to the beginning of the entry that it returns. Then, use ZipEntry.getSize() to determine how many bytes you should read using zipInputStream.read().

I actually have called getNextEntry() before getting to this snippet. I just added some clarifiaction to the question.
pkaeding
A: 

So what does zipEntry.getSize() return?

zipEntry.getSize() returns a reasonable number, 28689, in this case.
pkaeding
+3  A: 

You could implement your own wrapper around the ZipInputStream that ignores close() and hand that off to the third-party library.

thirdPartyLib.handleZipData(new CloseIgnoringInputStream(zipStream));


class CloseIgnoringInputStream extends InputStream
{
    private ZipInputStream stream;

    public CloseIgnoringInputStream(ZipInputStream inStream)
    {
     stream = inStream;
    }

    public int read() throws IOException {
     return stream.read();
    }

    public void close()
    {
     //ignore
    }

    public void reallyClose() throws IOException
    {
     stream.close();
    }
}
jt
This is an interesting idea....if nothing else works, I will probably try this.
pkaeding
+5  A: 

Your loop looks valid - what does the following code (just on it's own) return?

zipStream.read(tempBuffer)

if it's returning -1, then the zipStream is closed before you get it, and all bets are off. It's time to use your debugger and make sure what's being passed to you is actually valid.

When you call getNextEntry(), does it return a value, and is the data in the entry meaningful (i.e. does getCompressedSize() return a valid value)? IF you are just reading a Zip file that doesn't have read-ahead zip entries embedded, then ZipInputStream isn't going to work for you.

Some useful tidbits about the Zip format:

Each file embedded in a zip file has a header. This header can contain useful information (such as the compressed length of the stream, it's offset in the file, CRC) - or it can contain some magic values that basically say 'The information isn't in the stream header, you have to check the Zip post-amble'.

Each zip file then has a table that is attached to the end of the file that contains all of the zip entries, along with the real data. The table at the end is mandatory, and the values in it must be correct. In contrast, the values embedded in the stream do not have to be provided.

If you use ZipFile, it reads the table at the end of the zip. If you use ZipInputStream, I suspect that getNextEntry() attempts to use the entries embedded in the stream. If those values aren't specified, then ZipInputStream has no idea how long the stream might be. The inflate algorithm is self terminating (you actually don't need to know the uncompressed length of the output stream in order to fully recover the output), but it's possible that the Java version of this reader doesn't handle this situation very well.

I will say that it's fairly unusual to have a servlet returning a ZipInputStream (it's much more common to receive an inflatorInputStream if you are going to be receiving compressed content.

Kevin Day
ZipInputStream in java DOES NOT HANDLE THIS WELL.Thank you for posting this.
e5
A: 

Please try code bellow

private static byte[] getZipArchiveContent(File zipName) throws WorkflowServiceBusinessException {

  BufferedInputStream buffer = null;
  FileInputStream fileStream = null;
  ByteArrayOutputStream byteOut = null;
  byte data[] = new byte[BUFFER];

  try {
   try {
    fileStream = new FileInputStream(zipName);
    buffer = new BufferedInputStream(fileStream);
    byteOut = new ByteArrayOutputStream();

    int count;
    while((count = buffer.read(data, 0, BUFFER)) != -1) {
     byteOut.write(data, 0, count);
    }
   } catch(Exception e) {
    throw new WorkflowServiceBusinessException(e.getMessage(), e);
   } finally {
    if(null != fileStream) {
     fileStream.close();
    }
    if(null != buffer) {
     buffer.close();
    }
    if(null != byteOut) {
     byteOut.close();
    }
   }
  } catch(Exception e) {
   throw new WorkflowServiceBusinessException(e.getMessage(), e);
  }
  return byteOut.toByteArray();

 }
Dmytro