C# GZipStream 压缩数据但解压 returns 空流

C# GZipStream compressing data but decompression returns empty stream

我有以下代码:

    public static async Task<string> Compress(string inputString)
    {
        var bytes = Encoding.Unicode.GetBytes(inputString);
        await using var input = new MemoryStream(bytes);
        await using var output = new MemoryStream();
        await using var stream = new GZipStream(output, CompressionLevel.SmallestSize);

        await input.CopyToAsync(stream);

        return Convert.ToBase64String(output.ToArray());
    }

    public static async Task<string> Decompress(string inputString)
    {
        var bytes = Convert.FromBase64String(inputString);

        await using var output = new MemoryStream();
        await using var input = new MemoryStream(bytes);
        await using var stream = new GZipStream(input, CompressionMode.Decompress);

        await stream.CopyToAsync(output);
        await stream.FlushAsync();
        
        return Encoding.Unicode.GetString(output.ToArray());
    }

当我尝试压缩字符串'Hello World'时,压缩后的Base64编码字符串是'H4sIAAAAAAACCg=='

当我尝试解压缩 Base64 编码的字符串时 'H4sIAAAAAAACCg==' 方法解压缩 returns 一个空字符串。

您没有得到正确的压缩字符串。 output.ToArray()stream GZipStream 刷新之前被调用,因此 output MemoryStream 的内容还不是完整的压缩字节。 await input.CopyToAsync(stream);.

后需要加上 await stream.FlushAsync();

GZipStream 及其内部 DeflateStream 旨在在调用 Dispose 时隐式刷新其内部缓冲区(参见 https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs)。

对于C# 8.0 引入的不带花括号的语句,作用域受包含作用域的限制。在这种情况下,包含范围是方法本身,这意味着当方法最终退出时对象超出范围。因此,从 streamoutput 的隐式刷新发生在 Convert.ToBase64String(output.ToArray()).

之后

为避免这种情况,我们可以添加花括号来限制范围(参见下面的示例),或者通过显式刷新 stream(这并非对所有 .NET 版本都有效,参见 https://github.com/dotnet/runtime/commit/728aa671567d498c1acb6e13cb5cf4f7a883acf7.

public static async Task<string> Compress(string inputString)
{
    var bytes = Encoding.Unicode.GetBytes(inputString);
    
    await using (var input = new MemoryStream(bytes))
    {
        await using (var output = new MemoryStream())
        {
            await using (var stream = new GZipStream(output, CompressionLevel.SmallestSize))
            {
                await input.CopyToAsync(stream);
            }
        
            return Convert.ToBase64String(output.ToArray());
        }
    }
}

public static async Task<string> Decompress(string inputString)
{
    var bytes = Convert.FromBase64String(inputString);

    await using (var output = new MemoryStream())
    {
        await using (var input = new MemoryStream(bytes))
        {
            await using (var stream = new GZipStream(input, CompressionMode.Decompress))
            {
                await stream.CopyToAsync(output);
            }
        }

        return Encoding.Unicode.GetString(output.ToArray());
    }
}