0
votes

I've been trying to implement a compression method in one of my programs. I want it to take in a stream, compress it, and return the compressed stream (It returns a stream because I want to be able to pass the stream to another function without having to save it to a file and re-read it later). I had a working test version based on the msdn example for GZipStream, and this is what I came up with when I tried to convert it to taking in and returning streams:

public static Stream compress(Stream fileToCompress)
{
    using (MemoryStream compressedFileStream = new MemoryStream())
    {
        using (GZipStream compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress))
        {
            fileToCompress.CopyTo(compressionStream);
            return compressionStream;
        }
    }
}

Saving the returned stream to a file (in another method) results in a file of 0 bytes being created (pretty efficient compression, huh?).

I've tried looking for other solutions, but I haven't been able to find any that use streams, and my attempts to convert run into the same problem.

Edit: Just for the record, I have tried using DeflateStream to the same results.

EDIT2: Turns out it was the test program not saving properly. Thanks for the help.

2

2 Answers

2
votes

If your goal is to return the stream, you need to not put it in the using block.

Something like this:

public static Stream compress(Stream fileToCompress) {
    MemoryStream compressedFileStream = new MemoryStream();
    GZipStream compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress);
    fileToCompress.CopyTo(compressionStream);
    compressionStream.Seek(0, SeekOrigin.Begin); // Reset to stream start.
    return compressionStream;
}

Otherwise, when the stream leaves the using block, it calls Dispose() on the stream.

EDIT: Also, after a copy, the stream "pointer" is at the end. You need to set the pointer back to the start. Before you save - shown here.

EDIT: Removed all using blocks. If you need to release a stream, you can do it manually.

0
votes

You can skip using/Dispose calls as suggested by Steven Hansen, but it may be cleaner to return new copy of MemoryStream with compressed data.

public static Stream compress(Stream fileToCompress)
{
    using (MemoryStream compressedFileStream = new MemoryStream())
    {
        using (var compressionStream = new GZipStream(
             compressedFileStream, CompressionMode.Compress))
        {
            fileToCompress.CopyTo(compressionStream);
        }
        return new MemoryStream(compressionStream.ToArray());
    }
}

If you want to keep just single memory stream - remove using calls and don't forget to reposition the stream.

var compressedFileStream = new MemoryStream();
var compressionStream = new GZipStream(
           compressedFileStream, CompressionMode.Compress);
fileToCompress.CopyTo(compressionStream);
 // Flush to make sure all data written by compression stream.
compressionStream.Flush();
compressedFileStream.Position = 0;
return compressedFileStream;

Note that if you file is very large using temporary file to store compressed/uncompressed stream may be faster due to memory allocation strategy used in MemoryStream - try both and measure.