GZipStream
and DeflateStream
don’t buffer their input, so they will only compress the individual chunks
that are passed to Write
(or, worst-case, WriteByte
). Unless you can
guarantee that large blocks of data are being passed to GZipStream.Write
,
always wrap GZipStream
in a BufferedStream
when using CompressionMode.Compress
.
In fact, if you call only WriteByte
on a compressing GZipStream
, it will
create “compressed” output that’s almost double the size of
the input.
Experimentation determined that the optimal buffer size is 8K; above
this, no further compression is gained. So, to create a GZipStream
, use
a method similar to the following:
public static Stream CreateCompressingGZipStream(Stream stream, bool leaveOpen)
{
return new BufferedStream(
new GZipStream(stream, CompressionMode.Compress, leaveOpen),
8192);
}
This chart shows how the compressed output keeps getting smaller as the buffer size increases (and how writing just one or two bytes at a time almost doubles the 1MB input):
// determine best buffer size
foreach (int bufferSize in new[] { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1536,
2048, 4096, 6144, 8192, 16384, 32768, 65536, 131072, 262144 })
{
using (MemoryStream output = new MemoryStream())
{
using (GZipStream compressor = new GZipStream(output, CompressionMode.Compress,
leaveOpen : true))
using (BufferedStream buffer = new BufferedStream(compressor, bufferSize))
{
// write simple data that will compress easily
for (int i = 0; i < 1000000; i++)
buffer.WriteByte(0);
}
Console.WriteLine("Buffer Size: {0:n0}, Compressed size: {1:n0}",
bufferSize, output.Length);
}
}
Posted by Bradley Grainger on June 08, 2012