Question

I have the following piece of code:

MemoryStream resultStream = new MemoryStream();
string users = ""//Really long string goes here
BinaryFormatter bFormatter = new BinaryFormatter();
using (MemoryStream assignedUsersStream = new MemoryStream())
{
    bFormatter.Serialize(assignedUsersStream, users);
    assignedUsersStream.Position = 0;

    using (var compressionStream =
        new DeflateStream(resultStream, CompressionLevel.Optimal))
    {
        assignedUsersStream.CopyTo(compressionStream);

        Console.WriteLine("Compressed from {0} to {1} bytes.",
            assignedUsersStream.Length.ToString(),
            resultStream.Length.ToString());
    }
}            

the thing is that resultStream is always empty!

What am I doing wrong here?

Was it helpful?

Solution

Put your verification WriteLine outside of the using. The buffers haven't been flushed yet.

using (DeflateStream compressionStream = new DeflateStream(resultStream, CompressionLevel.Optimal))
{
    assignedUsersStream.CopyTo(compressionStream);

    //Console.WriteLine("Compressed from {0} to {1} bytes.",
    //       assignedUsersStream.Length.ToString(), resultStream.Length.ToString());
}

Console.WriteLine("Compressed from {0} to {1} bytes.",
     assignedUsersStream.Length, resultStream.ToArray().Length);

And aside, you don't need all those ToString()s in a writeline.

PS: All a BinaryFormatter does with a string is write the bytes with length prefix. If you don't need the prefix (my guess), it could become:

string users = "";//Really long string goes here
byte[] result;  

using (MemoryStream resultStream = new MemoryStream())
{
    using (DeflateStream compressionStream = new DeflateStream(resultStream,
             CompressionLevel.Optimal))
    {
        byte[] inBuffer = Encoding.UTF8.GetBytes(users);
        compressionStream.Write(inBuffer, 0, inBuffer.Length);
    }
    result = resultStream.ToArray();
}

The reverse is just as easy but you'll need an estimate of the maximum length to create the read-buffer:

string users2 = null;

using (MemoryStream resultStream = new MemoryStream(result))
{
    using (DeflateStream compressionStream = new  DeflateStream(resultStream,
            CompressionMode.Decompress))
    {
        byte[] outBuffer = new byte[2048];   // need an estimate here
        int length = compressionStream.Read(outBuffer, 0, outBuffer.Length);
        users2 = Encoding.UTF8.GetString(outBuffer, 0, length);                        
    }                    
}

OTHER TIPS

That is because the DeflateStream doesn't flush the data to the underlying stream until it is closed. After it is closed, resultStream will contain the compressed data. Note that by default, DeflateStream closes the underlying stream when it's closed, but you don't want that, so you need to pass true for the leaveOpen parameter. Also, you don't need 2 memory streams, you can just serialize directly to the compressionStream:

    string users = ""; //Really long string goes here
    BinaryFormatter bFormatter = new BinaryFormatter();
    using (MemoryStream resultStream = new MemoryStream())
    {
        using (DeflateStream compressionStream = new DeflateStream(resultStream, CompressionLevel.Optimal, true))
        {
            bFormatter.Serialize(compressionStream, users);
            Console.WriteLine(resultStream.Length); // 0 at this point
        }
        Console.WriteLine(resultStream.Length); // now contains the actual length
    } 

From the original answer (I don't have enough credits to vote down)

Put your control WriteLine outside of the using

This is incomplete and IMO therefore misleading. DeflateStream's Dispose(bool) implementation Closes the underlying resultStream when the DeflateStream is being Finalized after it's been Garbage Collected. When this happens, resultStream.Length will throw:

Unhandled Exception: System.ObjectDisposedException: Cannot access a closed Stream.

In other words, Thomas Levesque's note is critical: also set leaveOpen to true.

An interesting question with some good points raised by HH and TL.

I've come in late to this as I ran into this same problem and reading the conflicting answers. The initial response works! as does this test (over simplified to highlight the answer):

var inStream = new MemoryStream(data);
var outStream = new MemoryStream();
using (var compressor = new DeflateStream(outStream, CompressionLevel.Optimal))
{
    inStream.CopyTo(compressor);
}
return outStream;

where the using block needs to complete, triggering the compressor's Dispose, which internally Flush()es so that outStream will be guaranteed to contain the complete compressed data.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top