0

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.

Community
  • 1
  • 1

2 Answers2

2

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.

Steven Hansen
  • 3,189
  • 2
  • 16
  • 12
  • Note that your sample actually closes underlying stream which will cause the same "Reading/writing to disposed stream" exception. – Alexei Levenkov Feb 28 '14 at 04:23
  • I'm not 100% sure about the interaction of the `MemoryStream` and the `GZipStream` so I'll take your word for it. My edit removes the using. – Steven Hansen Feb 28 '14 at 04:26
  • Thanks for the quick response. I think I tried that before and it didn't work. I tried it again just to make sure, and I'm still running into the problem. Also @AlexeiLevenkov, it's not throwing any exceptions, it just returns an empty stream. – user3299958 Feb 28 '14 at 04:30
  • @user3299958 - I put sample that should work in my post. Make sure to read answers in this search http://stackoverflow.com/search?q=[c%23]+memorystream+zero+position that covers your 0 bytes file problem. – Alexei Levenkov Feb 28 '14 at 04:34
  • I edited to show code that seeks the `Stream` start. That will be needed before you save. – Steven Hansen Feb 28 '14 at 15:04
0

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.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179