35

我需要一种在 .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();
    }
4

2 回答 2

50

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

更一般地说,StreamIDisposable,所以你也应该是using每个......(是的,我知道这MemoryStream不会丢失任何数据,但如果你不养成这个习惯,它会和其他人一起咬你Stream)。

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())
    { ... }
}

[编辑:更新的重新评论]不是这样的using事情MemoryStream- 这总是一个有趣的事情,在栅栏的两边都有很多选票:但最终......

(rhetorical - we all know the answer...) How is MemoryStream implemented? is it a byte[] (owned by .NET)? is it a memory-mapped file (owned by the OS)?

The reason you aren't using it is because you are letting knowledge of internal implementation details change how you code against a public API - i.e. you just broke the laws of encapsulation. The public API says: I am IDisposable; you "own" me; therefore, it is your job to Dispose() me when you are through.

于 2008-11-07T04:46:41.780 回答
3

Also - keep in mind the DeflateStream in System.IO.Compression does not implement the most efficient deflate algorithm. If you like, there is an alternative to the BCL GZipStream and DeflateStream; it is implemented in a fully-managed library based on zlib code, that performs better than the built-in {Deflate,GZip}Stream in this respect. [ But you still need to Close() the stream to get the full bytestream. ]

These stream classes are shipped in the DotNetZlib assembly, available in the DotNetZip distribution at http://DotNetZip.codeplex.com/.

于 2009-01-04T03:56:31.557 回答