GZipStream和DeflateStream不会解压缩所有字节

时间:2008-11-07 04:39:18

标签: c# image compression zip bitmap

我需要一种在.net中压缩图像的方法,所以我研究了使用.net GZipStream类(或DeflateStream)。然而,我发现解压缩并不总是成功,有时图像会很好地解压缩,有时我会得到一个GDI +错误,表明某些东西已经损坏。

在调查问题后,我发现解压缩没有回复它压缩的所有字节。因此,如果我压缩2257974字节,我有时会回来2257870字节(实数)。

最有趣的是,有时它会起作用。所以我创建了这个只压缩10个字节的小测试方法,现在我根本没有回复任何东西。

我用压缩类GZipStream和DeflateStream尝试了它,我仔细检查了我的代码是否存在可能的错误。我甚至尝试将流定位到0并冲洗所有流但没有运气。

这是我的代码:

    public static void TestCompression()
    {
        byte[] test = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

        byte[] result = Decompress(Compress(test));

        // This will fail, result.Length is 0
        Debug.Assert(result.Length == test.Length);
    }

    public static byte[] Compress(byte[] data)
    {
        var compressedStream = new MemoryStream();
        var zipStream = new GZipStream(compressedStream, CompressionMode.Compress);
        zipStream.Write(data, 0, data.Length);
        return compressedStream.ToArray();
    }

    public static byte[] Decompress(byte[] data)
    {
        var compressedStream = new MemoryStream(data);
        var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress);
        var resultStream = new MemoryStream();

        var buffer = new byte[4096];
        int read;

        while ((read = zipStream.Read(buffer, 0, buffer.Length)) > 0) {
            resultStream.Write(buffer, 0, read);
        }

        return resultStream.ToArray();
    }

2 个答案:

答案 0 :(得分:49)

添加了要压缩的所有数据后,您需要Close() ZipStream;它在内部保留了一个需要写入的未写字节缓冲区(即使你Flush())。

更一般地说,StreamIDisposable,所以你也应该using ...(是的,我知道MemoryStream不会丢失任何数据,但是如果你没有养成这种习惯,那么它会让你感受到其他Stream s。。

public static byte[] Compress(byte[] data)
{
    using (var compressedStream = new MemoryStream())
    using (var zipStream = new GZipStream(compressedStream, CompressionMode.Compress))
    {
        zipStream.Write(data, 0, data.Length);
        zipStream.Close();
        return compressedStream.ToArray();
    }
}

public static byte[] Decompress(byte[] data)
{
    using(var compressedStream = new MemoryStream(data))
    using(var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress))
    using (var resultStream = new MemoryStream())
    { ... }
}

[编辑:更新评论] 不要usingMemoryStream这样的事情 - 这总是很有趣的,在围栏的两边都有很多选票:但是极端......

(修辞 - 我们都知道答案......)MemoryStream如何实施?它是一个byte [](由.NET拥有)?它是一个内存映射文件(由操作系统拥有)?

您不是using的原因是因为您让内部实现细节的知识改变了您对公共API进行编码的方式 - 即您刚刚违反了封装规则。公共API说:我是IDisposable;你欠我的;因此,当你通过时Dispose()我的工作就是你的工作。

答案 1 :(得分:3)

另外 - 请记住System.IO.Compression中的DeflateStream没有实现最有效的deflate算法。如果您愿意,可以选择BCL GZipStream和DeflateStream;它是在基于zlib代码的完全托管库中实现的,在这方面比内置的{Deflate,GZip} Stream表现更好。 [但你仍需要Close()流来获得完整的字节流。 ]

这些流类在DotNetZlib程序集中提供,可在http://DotNetZip.codeplex.com/的DotNetZip发行版中找到。