使用.NET 4.5进行.NET zlib解压缩

6
根据MSDN的资料,.Net 4.5中的System.IO.Compression是基于zlib实现的。
我现在正在尝试将我的当前基于interop的读取代码,从一个非.NET服务器上的zlib压缩流,改变为基于BCL的实现方法。
我的实现代码如下:
    var enc = new UTF8Encoding();            
        var readBytes = BufferSizeRaw;
        var outputBuffer = new byte[BufferSizeRaw];            
        var networkBuffer = _networkQueue.Take();
        var ms = new MemoryStream(networkBuffer.InputBuffer, 0, networkBuffer.UsedLength);
        using (Stream stream = new DeflateStream(ms, CompressionMode.Decompress))
            while (readBytes==BufferSizeRaw)
            {
                readBytes = stream.Read(outputBuffer, 0, outputBuffer.Length);                
                stringBuffer+= enc.GetString(outputBuffer, 0, readBytes);                
            }

我在DeflateStream的解压缩/读取的第一次调用时收到以下异常:
块长度与其补数不匹配
基于Interop的调用使用var result=inflate(ref zStyream, ZLibFlush.NoFlush; 有人尝试过相同的操作或者看到代码中的错误,还是我理解有误?
我也尝试了截断前两个字节,但没有成功。
前几个字节是20、202、177、13。
3个回答

19
上面的答案是正确的,但对于“为什么”并不十分清楚。原始ZLib流的前两个字节提供有关所使用压缩类型的详细信息。Microsoft的System.Io.Compression中的DeflateStream类不理解这些内容。修复方法如下:
using (MemoryStream ms = new MemoryStream(data))
{
    MemoryStream msInner = new MemoryStream();

    // Read past the first two bytes of the zlib header
    ms.Seek(2, SeekOrigin.Begin);

    using (DeflateStream z = new DeflateStream(ms, CompressionMode.Decompress))
    {
        z.CopyTo(msInner);

在这个例子中,data是一个带有原始Zlib文件的Byte[]。将其加载到MemoryStream中后,我们只需“寻找”超过前两个字节即可。
该文章解释了如果您查看原始字节,则Zlib标头的外观。 Zlib标头是什么样子?

9

你试图压缩的数据的前几个字节是什么?

你可能有zlib、gzip或原始deflate数据需要解码。

顺便说一句,我强烈建议你使用DotNetZip的zlib接口,而不是NET 4.5(或任何版本)的接口。NET 4.5在该接口中存在错误,微软已经宣布他们不会修复(!)。


需要纠正自己 - 它只适用于少数读取 - 很可能与内存流有关。 将进一步调查它。 - weismat
@MarkAdler 虽然DotNetZip在错误检测方面是最好的,但在我的一些实际场景中,它的速度大约比System.IO.Compression.DeflateStream慢两倍,比SharpZipLib慢三倍。但是SharpZipLib仍然存在一些损坏数据包无限解压缩的奇怪bug。 - tsul
你知道你是否在比较苹果和苹果吗?更改压缩设置可能会导致更大的速度差异。 - Mark Adler
6年过去了,MS不想修复的错误链接是否仍然404?我应该避免使用System.IO.Compression来处理zlib流吗? - Benni
1
@Benni 请查看上方的评论。据称4.7.2修复了所提到的错误。 - Mark Adler
显示剩余5条评论

5
这是ProVega答案的修订版本,它将字节数组转换为字符串:
using (var stream = new MemoryStream(bytes,2, bytes.Length - 2))
using (var inflater = new DeflateStream(stream, CompressionMode.Decompress))
using (var streamReader = new StreamReader(inflater))
{
    return streamReader.ReadToEnd();
}

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