35

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

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

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

于 2008-11-07T04:46:41.780 に答える
3

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

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

于 2009-01-04T03:56:31.557 に答える