使用C#压缩GZIP,然后在Python中解压缩失败

3

我有一个流程,其中一些数据(比如图像/视频)使用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

我当然进行了研究,找到了像这个Q&A之类的帖子,但没有什么帮助(例如,使用该答案会失败并显示gzip.BadGzipFile: Not a gzipped file (b'\x00\x00')。其它尝试则产生不同的gzip错误。

你能检查一下生成的Gzip文件是否有效吗?这个链接应该适用于Windows、Unix和Linux系统。 - MindSwipe
只是一个注意事项:在读取压缩/解压缩输出之前,您没有处理任何流。我不认为这会有任何影响,因为您首先明确地刷新了它们,所以我不认为这与您的问题有关,但作为最佳实践的一部分... - canton7
尝试缩小问题范围:比较Python和C#中解码后的字节(decodedBytesbytes),看看它们是否相同。 - canton7
我重新编写了你的代码,在通常需要释放流时进行了处理,并得到了一个不同(稍长)的结果:https://dotnetfiddle.net/bbriL7 。在C#中,这两种方法解码的结果是相同的,但我想知道较长的那个是否包含Python所期望的页脚... - canton7
确实,Flush方法不会调用_deflater.Finish(),但是Dispose方法会调用。我猜这是有道理的——在压缩结束时需要添加一个页脚,而Flush可能会被多次调用。 - canton7
1个回答

3

GzipStream需要在读取其输出之前进行处理。Gzip有一个页脚,需要添加到流的末尾,这是通过Dispose()(以及Close()添加的,但不是通过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的版本则不行。


Python使用与gzip相同的实现,从命令行或者从7-zip使用gzip都无法通过测试。有趣的是,.NET仍然可以处理它,并且似乎还可以处理一些缺失的头部信息。感谢您的帮助! - Camilo Terevinto
是的,由于页脚包含一个CRC,如果它丢失了,我会预计.NET也会出现故障。 - canton7

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接