web-dev-qa-db-ja.com

GZipStreamとDeflateStreamはすべてのバイトを解凍しません

.netで画像を圧縮する方法が必要だったので、.net GZipStreamクラス(またはDeflateStream)の使用を検討しました。ただし、解凍が常に成功するとは限らないことがわかりました。画像が正常に解凍される場合もあれば、何かが破損しているというGDI +エラーが発生する場合もあります。

問題を調査した後、解凍によって圧縮されたすべてのバイトが返されなかったことがわかりました。したがって、2257974バイトを圧縮すると、2257870バイト(実数)しか返されないことがあります。

最も面白いのは、時々それがうまくいくということです。だから私は10バイトだけを圧縮するこの小さなテストメソッドを作成しましたが、今は何も返されません。

圧縮クラスGZip​​Streamと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();
    }
35
Bobby Z

圧縮するすべてのデータを追加した後、Close()ZipStreamを実行する必要があります。書き込まれる必要のある未書き込みバイトのバッファを内部に保持します(Flush()であっても)。

より一般的には、StreamIDisposableであるため、それぞれusingである必要があります...(はい、MemoryStreamが失われることはないことを私は知っています任意のデータですが、この習慣を身に付けないと、他のStreams)に噛まれてしまいます。

_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-これは常に楽しいもので、フェンスの両側にたくさんの票があります:しかし最終的には...

(レトリック-私たちは皆答えを知っています...)MemoryStreamはどのように実装されていますか?それはbyte [](。NETが所有)ですか?それはメモリマップトファイル(OSが所有)ですか?

usingでない理由は、内部実装の詳細に関する知識によって、パブリックAPIに対するコーディング方法が変更されるためです。つまり、カプセル化の法則に違反しただけです。パブリックAPIによると:私はIDisposableです。私はあなたのものです;したがって、あなたが終わったときに私をDispose()するのはあなたの仕事です。

49
Marc Gravell

また、System.IO.CompressionのDeflateStreamは、最も効率的なdeflateアルゴリズムを実装していないことに注意してください。必要に応じて、BCLGZipStreamおよびDeflateStreamに代わるものがあります。これは、zlibコードに基づくフルマネージドライブラリに実装されており、この点で組み込みの{Deflate、GZip} Streamよりもパフォーマンスが優れています。 [ただし、完全なバイトストリームを取得するには、ストリームをClose()する必要があります。 ]

これらのストリームクラスは、DotNetZlibアセンブリで出荷され、DotNetZipディストリビューション( http://DotNetZip.codeplex.com/ )で入手できます。

3
Cheeso