Recently, I’m struggle with asp.net core 5 working with GZipStream. My request is easy: using GZipStream to compress a large text before sending it as response of asp.net content.
Here is my first guess code, which is not working:
//This code is not working.
public static async Task WriteWithGZipAndCompleteAsync(this HttpResponse response, string text)
{
response.StatusCode = 200;
response.ContentType = "application/gzip";
await using var gzip = new GZipStream(response.Body, CompressionLevel.Optimal, true);
await using var streamWriter = new StreamWriter(gzip, Encoding.UTF8, -1, true);
await streamWriter.WriteAsync(text);
await streamWriter.FlushAsync();
streamWriter.Close();
await gzip.FlushAsync();
gzip.Close();
await response.CompleteAsync();
}
It does send the text to the client but the gunzip reports “unexpected end of file” after decompressed all texts.
The second version is worse, it sends nothing at all:
//This code is not working either.
public static async Task WriteWithGZipAndCompleteAsync(this HttpResponse response, string text)
{
response.StatusCode = 200;
response.ContentType = "application/gzip";
using (var compressed = new MemoryStream())
{
using (var gzip = new GZipStream(compressed, CompressionLevel.Optimal, true))
{
var uncompressed = Encoding.UTF8.GetBytes(text);
gzip.Write(uncompressed);
gzip.Flush();
gzip.Close();
}
var result = compressed.ToArray();
response.Body.Write(result);
}
await response.CompleteAsync();
}
Finally, I get it work by this code below:
public static async Task WriteWithGZipAndCompleteAsync(this HttpResponse response, string text)
{
response.StatusCode = 200;
response.ContentType = "application/gzip";
await using var gzip = new GZipStream(response.Body, CompressionLevel.Optimal, true);
await using var streamWriter = new StreamWriter(gzip, Encoding.UTF8, -1, true);
await streamWriter.WriteAsync(text);
await streamWriter.FlushAsync();
await gzip.FlushAsync();
await response.Body.FlushAsync();
//WARNING: DO NOT CALL CompleteAsync, which will thrown an exception.
}
Conclusion:
- When calling Close() on instances of StreamWriter and GZipStream, the underlying stream will be closed, NO MATTER the value of leaveOpen specified. Uhh? Weird? But it’s true.
- Do not call response.CompleteAsync() after write data to response.Body.
- If Close() called on streams before, calling response.CompleteAsync() will thrown an ObjectDisposedException: Cannot access a closed stream.
- If Close() does not present, like the code above, calling response.CompleteAsync() will thrown an InvalidOperationException: Writing is not allowed after writer was completed.