在 c# 中使用 GZIP 压缩并在 python 中解压缩失败

Compressing with GZIP in c# and decompressing in python fails

我有一个流程,其中一些数据(如 image/video)使用 GZip 压缩,如下所示:

await using var outputStream = new MemoryStream();
await using var compressionStream = new GZipStream(outputStream, CompressionMode.Compress);

await compressionStream.WriteAsync(payload);
await compressionStream.FlushAsync();

outputStream.Position = 0;
return outputStream.ToArray()

以上代码并非来自我的团队,但如果需要可以更改。

如果我将输出转换为 base64 字符串并使用这个简单的代码测试解压缩它,它会完美运行:

var bytes = Convert.FromBase64String("H4sIAAAAAAAACirOz01VKEmtKAEAAAD//w=="); // "some text"
using var ms = new MemoryStream(bytes);
using var ds = new GZipStream(ms, CompressionMode.Decompress);
using var output = new MemoryStream();
ds.CopyTo(output);
ds.Flush();

var result = output.ToArray();

但是,我的要求是在 python 脚本中获取压缩的有效负载并在将其传递到另一个系统之前将其解压缩。我对python一点都不熟悉,所以我做了这个非常简单的脚本:

import base64
import gzip

encodedBase64 = "H4sIAAAAAAAACirOz01VKEmtKAEAAAD//w=="
decodedBytes = base64.standard_b64decode(encodedBase64)
decompressedBytes = gzip.decompress(decodedBytes)

以上失败:EOFError: Compressed file ended before the end-of-stream marker was reached

我当然做了研究并找到了像 这样的帖子,但没有任何帮助(例如,使用该答案失败 gzip.BadGzipFile: Not a gzipped file (b'\x00\x00')。其他尝试产生了不同的 gzip 错误。

GzipStream 需要在阅读其输出之前进行处理。 Gzip 有一个页脚需要添加到流的末尾,这是 added by Dispose() (and also by Close()) but not by Flush()。我想这是有道理的,因为 Flush() 在编码过程中可能会被多次调用,所以添加页脚的地方是错误的。

我重写了您的代码以在适当的位置处理对象,并且还删除了 async 因为您处理的是纯同步操作:

public static string Encode()
{
    var payload = Encoding.ASCII.GetBytes("some text");
    using (var outputStream = new MemoryStream())
    {
        using (var compressionStream = new GZipStream(outputStream, CompressionMode.Compress))
        {
            compressionStream.Write(payload);
        }
        var result = outputStream.ToArray();
        return Convert.ToBase64String(result);
    }   
}

这会产生输出

H4sIAAAAAAAAAyvOz01VKEmtKAEAur26TwkAAAA=

比您看到的输出稍长,表明它包含 Python 期望的页脚。然而,它仍然解码为相同的结果,至少在 .NET 中是这样。

有趣的是,.NET 的 GzipStream 对缺少的页脚是健壮的,但 Python 的版本不是。