System.IO.MemoryStream Quirks Image

System.IO.MemoryStream Quirks

December 11, 2016      .net Programming

Today I was using a MemoryStream to create an in-memory ZIP archive. After properly using the System.IO.Compression namespace ZipArchive and ZipArchiveEntry objects I was running into corrupted ZIP output due to a quirk I discovered in the System.IO.MemoryStream object.

For those that don't know, a MemoryStream allow file-like access to a series of bytes that reside in system RAM only. This is ideal for operations that never need to be persisted to permanent storage.Oddly, MemoryStream has two methods to access its internal byte buffer - GetBuffer and ToArray. At first glance one would expect these to be identical but they act quite differently.

According to Reflector, GetBuffer looks like this:

public virtual byte[] GetBuffer()
        {
            if (!this._exposable)
            {
                throw new UnauthorizedAccessException(Environment.GetResourceString("UnauthorizedAccess_MemStreamBuffer"));
            }
            return this._buffer;
        }

 

In contrast, the ToArray method uses this code:

public virtual byte[] ToArray()
        {
            byte[] numArray = new byte[this._length - this._origin];
            Buffer.InternalBlockCopy(this._buffer, this._origin, numArray, 0, this._length - this._origin);
            return numArray;
        }

 

So apparently, the internal buffer (this._buffer)does NOT accurately represent all of the bytes in RAM. Go figure!

This example shows the difference:

MemoryStream ms = new MemoryStream();
using (ZipArchive zip = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
    ZipArchiveEntry entry = zip.CreateEntry("Test File");
    using (FileStream file = File.OpenRead(@"C:\Temp\Test File.txt"))
    {
        file.Seek(0, SeekOrigin.Begin);
        using (Stream entryStream = entry.Open())
            file.CopyTo(entryStream);
    }
}
ms.Seek(0, SeekOrigin.Begin);
ms.GetBuffer(); // FAILS
ms.ToArray(); // SUCCESS

 

I have never encountered this issue before so I thought I would share it with you to hopefully save you the hours I spent figuring it out.

Share It