2

Hello my problem is that I need to compress MemoryStream with GZipStream into MemoryStream if possible into the same MemoryStream that method gets as parameter. If this can't be achieved how can I make it so it is the most memory efficient.


Here is my current method and it is giving me System.NotSupportedException: 'Stream does not support reading.' for compress.CopyTo

public static void GZipCompress(MemoryStream memoryStream)
{
    using (GZipStream compress = new GZipStream(memoryStream, CompressionMode.Compress))
    {
        compress.CopyTo(memoryStream);
    }
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Moja Pica
  • 23
  • 5
  • Most memory efficient is likely to avoid the output memory stream completely. Are you going to be streaming the compressed data somewhere else, like a file, a socket or a string? Then use that directly instead of a memory intermediate. (The same applies to the input, of course.) – Jeroen Mostert Aug 23 '18 at 15:41
  • I have to put MemoryStream to Stream which is writing to Ftp. – Moja Pica Aug 23 '18 at 15:47
  • Possible duplicate of [How do I use GZipStream with System.IO.MemoryStream?](https://stackoverflow.com/questions/3722192/how-do-i-use-gzipstream-with-system-io-memorystream) – Liam Aug 23 '18 at 15:48

1 Answers1

1

You can't copy the stream to itself, at least not without a lot of work. Just allocating a new MemoryStream for the compressed data is simple and reasonably efficient. eg

public MemoryStream GZipCompress(MemoryStream memoryStream)
{
    var newStream = new MemoryStream((int)memoryStream.Length / 2); //set to estimate of compression ratio

    using (GZipStream compress = new GZipStream(newStream, CompressionMode.Compress))
    {
        memoryStream.CopyTo(compress);
    }
    newStream.Position = 0;
    return newStream;
}

Here's an untested idea for how to perform in-place compression of a MemoryStream.

public void GZipCompress(MemoryStream memoryStream)
{
    var buf = new byte[1024 * 64];
    int writePos = 0;

    using (GZipStream compress = new GZipStream(memoryStream, CompressionMode.Compress))
    {
        while (true)
        {
            var br = compress.Read(buf, 0, buf.Length);
            if (br == 0) //end of stream
            {
                break;
            }
            var readPos = memoryStream.Position;
            memoryStream.Position = writePos;
            memoryStream.Write(buf, 0, br);
            writePos += br;

            if (memoryStream.Position > readPos)
            {
                throw new InvalidOperationException("Overlapping writes corrupted the stream");
            }
            memoryStream.Position = readPos;
        }
    }
    memoryStream.SetLength(writePos);
    memoryStream.Position = 0;
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • But then I would have 2 memory streams in memory woudln't I? What if they are very big? – Moja Pica Aug 23 '18 at 15:49
  • Then you would have two big streams :). If you _really want to_ you could probably reuse the main stream. it will blow up if any block of data fails to actually compress, as mathematically some blocks must. But see the edit. – David Browne - Microsoft Aug 23 '18 at 15:57
  • Thanks for help :) – Moja Pica Aug 23 '18 at 16:04
  • 1
    I tried your code not the one replacing memoryStream with zipped one but your first snippet and it still gives me error **System.NotSupportedException: 'Stream does not support reading.'** compress.CopyTo(newStream); – Moja Pica Aug 23 '18 at 16:16