Related to Handling HTTP ContentEncoding "deflate", I'd like to know how to use an OutputStream to inflate both gzip and deflate streams. Here's why:
I have a class that fetches resources from a web server (think wget, but in Java). I have it strictly-enforcing the Content-Length of the response and I'd like to keep that enforcement. So, what I'd like to do is read a specific number of bytes from the response (which I'm already doing) but have it generate more bytes if the response has been compressed.
I have this working for deflate responses like this:
OutputStream out = System.out;
out = new InflateOutputStream(out);
// repeatedly:
out.write(compressedBytesFromResponse);
I'd like to be able to do the same thing with gzip responses, but without a GunzipOutputStream, I'm not sure what to do, next.
Update
I was considering building something like this, but it seemed completely insane. Perhaps that is the only way to use an OutputStream to inflate my data.
Answering my own question:
There are two possibilities, here: gunzip on output (e.g. use
GunzipOutputStream, not provided by the Java API), or gunzip on input (e.g. useGZIPInputStream, provided by the Java API) plus enforce the Content-Length during the reads.I have done both, and I think I prefer the latter because a) it does not require a separate thread to be launched to pump bytes from
PipedOutputStreamto aPipedIOnputStreamand b) (a corollary, I guess) it does not have such a threat of race-conditions and other synchronization issues.First, here is my implementation of
LimitedInputStream, which allows me to wrap the input stream and enforce a limit on the amount of data read. Note that I also have aBigLimitedInputStreamthat uses aBigIntegercount to support Content-Length values greater thanLong.MAX_LONG:Using the above class to wrap the
InputStreamobtained from theHttpURLConnectionallows me to simplify the existing code I had to read the precise number of bytes mentioned in theContent-Lengthheader and just blindly copy input to output. I then wrap the input stream (already wrapped in theLimitedInputStream) in aGZIPInputStreamto decompress, and just pump the bytes from (doubly-wrapped) input to output.The less-straightforward route is to pursue my original line of though: to wrap the OutputStream using (what turned out to be) an awkward class:
GunzipOutputStream. I have written aGunzipOutputStreamwhich uses an internal thread to pump bytes through a pair of piped streams. It's ugly, and it's based upon code from OpenRDF'sGunzipOutputStream. I think mine is a bit simpler:Again, this works, but it seems fairly ... fragile.
In the end, I re-factored my code to use the
LimitedInputStreamandGZIPInputStreamand didn't use theGunzipOutputStream. If the Java API provided aGunzipOutputStream, it would have been great. But it doesn't, and without writing a "native" gunzip algorithm, implementing your ownGunzipOutputStreamstretches the limits of propriety.