Zlib 压缩不兼容 C 与 C# 实现

Zlib compression incompatibile C vs C# implementations

我正在尝试使用 2 个与 zlib 算法兼容的最合法的库解压缩在 C# 中使用 zlib 算法压缩的数据,但我抛出了类似的异常。

使用 DotNetZip:

Ionic.Zlib.ZlibException: Bad state (invalid stored block lengths)

使用Zlib.Net:

inflate: invalid stored block lenghts

但在 linux 上使用与 zlib-flate 命令相同的输入数据,仅使用默认参数,效果很好,解压缩时没有任何警告(输出正确):

zlib-flate -uncompress < ./dbgZlib

有什么建议可以用来在 C# 中解压缩此数据,或者为什么在这种情况下实际上解压缩失败?

十六进制压缩数据:

root@localhost:~# od -t x1 -An ./dbgZlib |tr -d '\n '
789c626063520b666060606262d26160d05307329999e70a6400e93c2066644080cf8c938c0c0c4d0d0d0d2d839c437c02dcfd0c0c0c11d28ea121013e7e41860ce18e210640e06810141669c080051840012eb970d790800090f99eee409ea189025e806c8e8b5354a89b13d81c136ca60f3a000e5fd6af0fb14a3221873e96400506374cd6c7d52dc8d98980657e7e06460ace0a4ce86e80da9f0249030edf816c16481ab06b60404f03931169c0cdc728c0db0fd928681a3042a481480347336c6e21320d78fb8155195a9090067ca3420387771a400a546aa70100000000ffff

压缩数据为 base64:

root@localhost:~# base64 ./dbgZlib
eJxiYGNSC2ZgYGBiYtJhYNBTBzKZmecKZADpPCBmZECAz4yTjAwMTQ0NDS2DnEN8Atz9DAwMEdKO
oSEBPn5BhgzhjiEGQOBoEBQWacCABRhAAS65cNeQgACQ+Z7uQJ6hiQJegGyOi1NUqJsT2BwTbKYP
OgAOX9avD7FKMiGHPpZABQY3TNbH1S3I2YmAZX5+BkYKzgpM6G6A2p8CSQMO34FsFkgasGtgQE8D
kxFpwM3HKMDbD9koaBowQqSBSANHM2xuITINePuBVRlakJAGfKNCA4d3GkAKVGqnAQAAAAD//w==

解压后的数据,base64编码如下:

root@localhost:~# zlib-flate -uncompress < ./dbgZlib | base64
AAYCJlMAAAACAgIsAAAuJwAAAAMDnRBoAAAAbgAAAAEAAAAAAAAAAAAAAPMBkjIwMTUxMTE5UkNU
TFBHTjAwMQAAAAAAAAAAAABBVVRQTE5SMQBXQVQwMDAwQTBSVlkwAAAAAAAAAAAAAAAAAAAAAAAA
AAAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAMDAwMFdFVFBQTFBHTklHMDAwMTQgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAwMAAAAAAAAAAAAABEQlpVRkIAAAAAMDQAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAX14QAAAAAAAAAA
AAAAAAAAAAAAAAAAAAIBAAAAAAAAAAAAAABBVDAwMDBBMFJWWTAAAAAAAAAAUExOAAAAAAAAAABM
RUZSQ0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATk4wMiBDIAIAAAAAAAAAAAAAAAAA
AAABBfXhAAAAAGQCAgIsAABA9wAAAAQDnRBoAAA+gAAAAAEAAAAAAAAAAAAAAPMBkzIwMTUxMTE5
UkNGTDJQS04AAAAAAAAAAAAAAABBVVRQTE5SMgBXQVQwMDAwQTBZMEE2AAAAAAAAAAAAAAAAAAAA
AAAAAAAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAMDAwMFdFVFBQTFBLTjAwMDAwMTggICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAwMAAAAAAAAAAAAABETVpVUUIAAAAAMDQAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAX14QAAAAAA
AAAAAAAAAAAAAAAAAAAAAAIBAAAAAAAAAAAAAABBVDAwMDBBMFkwQTYAAAAAAAAAUExOAAAAAAAA
AABMRUZSQ0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATk4wMiBDIAIAAAAAAAAAAAAA
AAAAAAABBfXhAAAAAGQ=

问题是您正在使用 zlib-flate 作为 general-purpose 压缩算法,根据 manpage,您不应该这样做:

This program should not be used as a general purpose compression tool. Use something like gzip(1) instead.

所以也许您应该遵循您的工具给出的说明,不要将它们用于不适合它们的用途。使用 gzipSystem.IO.Compression.GZipStream 代替,它更简单,尤其是当您正在寻找 cross-platform 兼容的压缩算法时。

也就是说……

无法扩充数据的原因是它缺少正确的 GZIP header。如果你加上正确的header就可以得到可以解压的东西

例如:

public static byte[] DecompressZLibRaw(byte[] bCompressed)
{
    byte[] bHdr = new byte[] { 0x1F, 0x8b, 0x08, 0, 0, 0, 0, 0 };

    using (var sOutput = new MemoryStream()) 
    using (var sCompressed = new MemoryStream())
    {
        sCompressed.Write(bHdr, 0, bHdr.Length);
        sCompressed.Write(bCompressed, 0, bCompressed.Length);
        sCompressed.Position = 0;
        using (var decomp = new GZipStream(sCompressed, CompressionMode.Decompress))
        {
            decomp.CopyTo(sOutput);
        }
        return sOutput.ToArray();
    }
}

添加 header 让一切变得不同。


注意:10 字节的 GZIP header 中有两个字节未从您的源代码中删除。这些通常用于存储压缩标志和源文件系统。在您提供的压缩数据中,它们是无效值。此外,文件页脚被缩写为 5 个字节而不是 8 个字节……所有这些实际上都不是解压缩所必需的。这可能与联机帮助页上说不要将其用于一般压缩的原因有很大关系。

您提供的流不完整。您似乎在 C# 代码中以 Z_SYNC_FLUSHZ_FULL_FLUSH 结束它,而不是像您应该的那样 Z_FINISH 。那是导致错误的原因。如果你正确地终止流,你就不会有问题。

zlib-flate 只是忽略了那个错误。

如果您无法控制流的生成,您仍然可以使用 zlib 解压缩其中的内容。您只需要在较低级别使用它,您可以在其中操作数据块并在给定输入的情况下获得可用的解压缩数据。