【问题标题】:How do you use a DeflateStream on part of a file?如何在文件的一部分上使用 DeflateStream?
【发布时间】:2010-10-20 05:48:59
【问题描述】:

我正在研究my other question 的解决方案,它正在读取 PNG 的“zTXt”块中的数据。我正在定位文件中的块,并阅读 zTXt 的关键字。我无法阅读 zTXt 的压缩部分。我以前从未使用过 DeflateStream 对象,并且遇到了一些麻烦。读取时,似乎期望长度参数为“未压缩”字节。然而,就我而言,我只知道“压缩”字节中的数据长度。为了希望解决这个问题,我将所有需要解压缩的数据放入 MemoryStream 中,然后使用 DeflateStream 进行“读取结束”。现在这简直太棒了,除了它抛出一个 InvalidDataException 消息“块长度与它的补码不匹配”。现在我不知道这意味着什么。可能出了什么问题?

块的格式是 4 个字节的 ID(“zTXt”),一个大端序 32 位 int 的数据长度、数据,最后是一个我现在忽略的 CRC32 校验和。

zTXt 块的格式首先是以 null 结尾(字符串作为关键字),然后是压缩方法的一个字节(始终为 0,DEFLATE 方法),其余数据为压缩文本。

我的方法接收一个新的 FileStream,并返回一个带有 zTXt 关键字和数据的字典。

现在是怪物:

public static List<KeyValuePair<string, string>> GetZtxt(FileStream stream)
{
    var ret = new List<KeyValuePair<string, string>>();
    try {
        stream.Position = 0;
        var br = new BinaryReader(stream, Encoding.ASCII);
        var head = br.ReadBytes(8); // The header is the same for all PNGs.
        if (!head.SequenceEqual(new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A })) return null; // Not a PNG.
        while (stream.Position < stream.Length) {
            int len; // Length of chunk data.
            if (BitConverter.IsLittleEndian)
                len = BitConverter.ToInt32(br.ReadBytes(4).Reverse().ToArray(), 0);
            else
                len = br.ReadInt32();

            char[] cName = br.ReadChars(4); // The chunk type.
            if (cName.SequenceEqual(new[] { 'z', 'T', 'X', 't' })) {
                var sb = new StringBuilder(); // Builds the null-terminated keyword associated with the chunk.
                char c = br.ReadChar();
                do {
                    sb.Append(c);
                    c = br.ReadChar();
                }
                while (c != '\0');
                byte method = br.ReadByte(); // The compression method.  Should always be 0. (DEFLATE method.)
                if (method != 0) {
                    stream.Seek(len - sb.Length + 3, SeekOrigin.Current); // If not 0, skip the rest of the chunk.
                    continue;
                }
                var data = br.ReadBytes(len - sb.Length - 1); // Rest of the chunk data...
                var ms = new MemoryStream(data, 0, data.Length); // ...in a MemoryStream...
                var ds = new DeflateStream(ms, CompressionMode.Decompress); // ...read by a DeflateStream...
                var sr = new StreamReader(ds); // ... and a StreamReader.  Yeesh.
                var str = sr.ReadToEnd(); // !!! InvalidDataException !!!
                ret.Add(new KeyValuePair<string, string>(sb.ToString(), str));
                stream.Seek(4, SeekOrigin.Current); // Skip the CRC check.
            }
            else {
                stream.Seek(len + 4, SeekOrigin.Current); // Skip the rest of the chunk.
            }
        }
    }
    catch (IOException) { }
    catch (InvalidDataException) { }
    catch (ArgumentOutOfRangeException) { }
    return ret;
}

一旦解决了这个问题,我需要编写一个函数,将这些 zTXt 块添加到文件中。所以希望一旦解决这个问题,我就能理解 DeflateStream 的工作原理。

非常感谢!

【问题讨论】:

    标签: c# .net compression png deflate


    【解决方案1】:

    经过这么长时间,我终于找到了问题所在。数据采用 zlib 格式,存储的数据比单独使用 DEFLATE 多一点。如果我刚刚在获得压缩数据之前读取了 2 个额外字节,则文件被正确读取。

    this feedback page。 (我没有提交那个。)

    我现在想知道。这两个字节的值分别是 0x78 和 0x9C。如果我发现其他值,我应该假设 DEFLATE 会失败吗?

    【讨论】:

    • faqs.org/rfcs/rfc1950.html 描述了 ZLib 格式。第一个字节0x78称为CMF,其值表示CM=8,CINFO=7。CM=8表示“deflate”压缩方式,窗口大小最大为32K。这是 gzip 和 PNG 使用的方法。 CINFO = 7 表示 32K 窗口大小。 0x9C称为FLG,其值表示FLEVEL = 2,CHECK = 28。解压不需要FLEVEL中的信息;它表明重新压缩是否值得。 CHECK 设置为任何必要的值,这样 CMF*256 + FLG 是 31 的倍数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-11
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    • 2017-02-05
    • 2019-08-08
    • 2012-07-26
    相关资源
    最近更新 更多