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.