title: Deflate (ZIP) compressing on the fly in Java using InputStream and OutputStream abstraction only. Possible?
body: <p>I'm currently trying to write a custom streams proxy (let's call it in that way) that can change the content from the given input stream and produce a modified, if necessary, output. This requirement is really necessary because sometimes I have to modify the streams in my application (e.g. compress the data truly <em>on the fly</em>). The following class is pretty easy and it uses internal buffering.</p>
  
  <pre class="lang-java prettyprint-override"><code>private static class ProxyInputStream extends InputStream {
  
      private final InputStream iStream;
      private final byte[] iBuffer = new byte[512];
  
      private int iBufferedBytes;
  
      private final ByteArrayOutputStream oBufferStream;
      private final OutputStream oStream;
  
      private byte[] oBuffer = emptyPrimitiveByteArray;
      private int oBufferIndex;
  
      ProxyInputStream(InputStream iStream, IFunction&lt;OutputStream, ByteArrayOutputStream&gt; oStreamFactory) {
          this.iStream = iStream;
          oBufferStream = new ByteArrayOutputStream(512);
          oStream = oStreamFactory.evaluate(oBufferStream);
      }
  
      @Override
      public int read() throws IOException {
          if ( oBufferIndex == oBuffer.length ) {
              iBufferedBytes = iStream.read(iBuffer);
              if ( iBufferedBytes == -1 ) {
                  return -1;
              }
              oBufferIndex = 0;
              oStream.write(iBuffer, 0, iBufferedBytes);
              oStream.flush();
              oBuffer = oBufferStream.toByteArray();
              oBufferStream.reset();
          }
          return oBuffer[oBufferIndex++];
      }
  
  }
  </code></pre>
  
  <p>Let's assume we also have a sample test output stream that simply adds a space character before every written byte ("abc" -> " a b c") like this:</p>
  
  <pre class="lang-java prettyprint-override"><code>private static class SpacingOutputStream extends OutputStream {
  
      private final OutputStream outputStream;
  
      SpacingOutputStream(OutputStream outputStream) {
          this.outputStream = outputStream;
      }
  
      @Override
      public void write(int b) throws IOException {
          outputStream.write(' ');
          outputStream.write(b);
      }
  
  }
  </code></pre>
  
  <p>And the following test method:</p>
  
  <pre class="lang-java prettyprint-override"><code>private static void test(final boolean useDeflater) throws IOException {
      final FileInputStream input = new FileInputStream(SOURCE);
      final IFunction&lt;OutputStream, ByteArrayOutputStream&gt; outputFactory = new IFunction&lt;OutputStream, ByteArrayOutputStream&gt;() {
          @Override
          public OutputStream evaluate(ByteArrayOutputStream outputStream) {
              return useDeflater ? new DeflaterOutputStream(outputStream) : new SpacingOutputStream(outputStream);
          }
      };
      final InputStream proxyInput = new ProxyInputStream(input, outputFactory);
      final OutputStream output = new FileOutputStream(SOURCE + ".~" + useDeflater);
      int c;
      while ( (c = proxyInput.read()) != -1 ) {
          output.write(c);
      }
      output.close();
      proxyInput.close();
  }
  </code></pre>
  
  <p>This test method simply reads the file content and writes it to another stream, that's probably can be modified somehow. If the test method is running with <code>useDeflater=false</code>, the expected approach works fine as it's expected. But if the test method is invoked with the <code>useDeflater</code> set on, it behaves really strange and simply writes almost nothing (if omit the header <code>78 9C</code>). I suspect that the deflater class may not be designed to meet the approach I like to use, but I always believed that ZIP format and the deflate compression are designed to work on-fly.</p>
  
  <p>Probably I'm wrong at some point with the specifics of the deflate compression algorithm. What do I really miss?.. Perhaps there could be another approach to write a "streams proxy" to behave exactly as I want it to work... How can I compress the data on the fly being limited with the streams only?</p>
  
  <p>Thanks in advance.</p>
  
  <hr>
  
  <p>UPD: The following basic version works pretty nice with deflater and inflater:</p>
  
  <pre class="lang-java prettyprint-override"><code>public final class ProxyInputStream&lt;OS extends OutputStream&gt; extends InputStream {
  
  private static final int INPUT_BUFFER_SIZE = 512;
  private static final int OUTPUT_BUFFER_SIZE = 512;
  
  private final InputStream iStream;
  private final byte[] iBuffer = new byte[INPUT_BUFFER_SIZE];
  private final ByteArrayOutputStream oBufferStream;
  private final OS oStream;
  private final IProxyInputStreamListener&lt;OS&gt; listener;
  
  private byte[] oBuffer = emptyPrimitiveByteArray;
  private int oBufferIndex;
  private boolean endOfStream;
  
  private ProxyInputStream(InputStream iStream, IFunction&lt;OS, ByteArrayOutputStream&gt; oStreamFactory, IProxyInputStreamListener&lt;OS&gt; listener) {
      this.iStream = iStream;
      oBufferStream = new ByteArrayOutputStream(OUTPUT_BUFFER_SIZE);
      oStream = oStreamFactory.evaluate(oBufferStream);
      this.listener = listener;
  }
  
  public static &lt;OS extends OutputStream&gt; ProxyInputStream&lt;OS&gt; proxyInputStream(InputStream iStream, IFunction&lt;OS, ByteArrayOutputStream&gt; oStreamFactory, IProxyInputStreamListener&lt;OS&gt; listener) {
      return new ProxyInputStream&lt;OS&gt;(iStream, oStreamFactory, listener);
  }
  
  @Override
  public int read() throws IOException {
      if ( oBufferIndex == oBuffer.length ) {
          if ( endOfStream ) {
              return -1;
          } else {
              oBufferIndex = 0;
              do {
                  final int iBufferedBytes = iStream.read(iBuffer);
                  if ( iBufferedBytes == -1 ) {
                      if ( listener != null ) {
                          listener.afterEndOfStream(oStream);
                      }
                      endOfStream = true;
                      break;
                  }
                  oStream.write(iBuffer, 0, iBufferedBytes);
                  oStream.flush();
              } while ( oBufferStream.size() == 0 );
              oBuffer = oBufferStream.toByteArray();
              oBufferStream.reset();
          }
      }
      return !endOfStream || oBuffer.length != 0 ? (int) oBuffer[oBufferIndex++] &amp; 0xFF : -1;
  }
  </code></pre>
  
  <p>}</p>