Search code examples
javacompressionbytearrayoutputstreamgzipoutputstream

java wrap GZIPOutputStream & ByteArrayOutputStream together - what am I doing wrong?


Main.java

import java.io.IOException;

public class Main
{

    private final CompressedOutputStream    m_cos;

    public static void main(String[] args)
    {
        try
        {
            final Main m = new Main(new CompressedOutputStream());
            m.run();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    public Main(final CompressedOutputStream cos)
    {
        this.m_cos = cos;
    }

    private void trace(final String format, Object... args)
    {
        final String output = String.format(format, args);
        System.out.println(output);
    }

    public void run()
    {
        this.first_write();
        this.second_write();
        this.trace("CRC32 of data is %x", this.m_cos.get_crc32());
    }

    private void add_data(final byte[] data)
    {
        try
        {
            this.m_cos.write(data); // STEPPING INTO THIS IN DEBUG DOESN'T LEAD ANYWHERE!
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    public void first_write()
    {
        final String[] text_lines = new String[] { "Hello world!",
                "My name is Joe.", "Nice to meet.", "Where are you from?",
                "I'm from Congo" };

        for (final String text_line : text_lines)
        {
            final byte[] data = text_line.getBytes();
            this.add_data(data);
        }

        final byte[] compressed_data = this.m_cos.get_compressed_data();
        trace("Compressed data length (so far) is %d bytes",
                compressed_data.length);
    }

    public void second_write()
    {
        final byte[] additional_data = new byte[] { 0x00, 0x01, 0x02, 0x03,
                0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
        this.add_data(additional_data);

        final byte[] total_compressed_data = this.m_cos.get_compressed_data();
        trace("Total compressed data length is %d bytes",
                total_compressed_data.length);
    }
}

CompressedOutputStream.java

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.Deflater;
import java.util.zip.GZIPOutputStream;

public class CompressedOutputStream extends GZIPOutputStream
{

    private final ByteArrayOutputStream m_out;

    public CompressedOutputStream() throws IOException
    {
        this(32);
    }

    public CompressedOutputStream(int size) throws IOException
    {
        super(new ByteArrayOutputStream(size), size);
        this.m_out = (ByteArrayOutputStream) this.out;
        this.def.setLevel(Deflater.BEST_COMPRESSION);
    }

    public long get_crc32()
    {
        return this.crc.getValue();
    }

    public byte[] get_compressed_data()
    {
        return this.m_out.toByteArray();
    }
}

So I have this code which tries to combine GZIPOutputStream with ByteArrayOutputStream.

For some reason all I'm getting is the same 10 bytes.
Getting this output:

Compressed data length (so far) is 10 bytes
Total compressed data length is 10 bytes
CRC32 of data is 4550d94d

It seems like calling to write() ends-up with an abstractive function, i.e it doesn't get anywhere, nor anything is written.
I want it to be written, compressed, on the fly, and be able to take the compressed bytes at later time.

What am I missing here? Seems trivial but no so.

Edit #1: My final goal
Just to clarify: At the end all I need is an in-memory buffer to which I will write, in chunks and not in a sequence, and at some point when it reaches X bytes I'll be able to take these, compressed & checksum'ed, bytes (to write them somewhere, not a standard Java stream).


Solution

  • GZIPOutputStream does some internal buffering before it passes the data along to the next stream in the chain (in this case the ByteArrayOutputStream). Thus you have to close() the stream before you can read the bytes back out. For example, I made the following modification to second_write():

    this.m_cos.close();
    final byte[] total_compressed_data = this.m_cos.get_compressed_data();
    trace("Total compressed data length is %d bytes", total_compressed_data.length);
    

    This gave me the output:

    Compressed data length (so far) is 10 bytes
    Total compressed data length is 98 bytes
    CRC32 of data is 4550d94d

    If you can't afford to close the stream, there might be a way to forcibly flush the internal buffer without closing. (flush() itself doesn't do that though, I tried).