如何在文件的一部分上使用DeflateStream?

3
我正在解决我的另一个问题,该问题涉及读取PNG文件中“ zTXt”块中的数据。我已经定位到文件中的块,并读取了zTXt的关键字。但是,我无法读取zTXt的压缩部分。我以前从未使用过DeflateStream对象,并且遇到了一些麻烦。在读取时,它似乎期望长度参数为“未压缩”字节。但在我的情况下,我只知道“压缩”字节中数据的长度。为了希望克服这个问题,我将所有需要解压缩的数据放入MemoryStream中,然后使用DeflateStream进行“读取到结束”。但是,这会抛出一个InvalidDataException异常,其中包含消息“块长度与其补码不匹配。”我现在不知道这意味着什么。可能出了什么问题?
块的格式是ID(“zTXt”)的4个字节,数据长度的大端32位整数,数据,最后是CRC32校验和,我现在忽略它。

zTXt块的格式首先是以空字符结尾的字符串作为关键字,然后是一个字节用于压缩方法(始终为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是如何工作的。

非常感谢!!

1个回答

7
经过这么长时间,我终于找到了问题所在。数据是使用zlib格式存储的,与仅使用DEFLATE相比,zlib格式存储的数据更多。如果我在获取压缩数据之前读取额外的2个字节,则可以正确地读取文件。
请参见this feedback page。(我没有提交那个反馈。)
我现在想知道这两个字节的值分别为0x78和0x9C。如果我发现其他值,是否应该假定DEFLATE会失败?

4
http://www.faqs.org/rfcs/rfc1950.html 描述了ZLib格式。第一个字节0x78称为CMF,值表示CM = 8,CINFO = 7。 CM = 8表示使用最大32K窗口大小的“deflate”压缩方法。这是gzip和PNG使用的方法。 CINFO = 7表示32K窗口大小。0x9C称为FLG,值表示FLEVEL = 2,CHECK = 28。 FLEVEL中的信息在解压缩时不需要;它存在是为了指示是否值得重新压缩。 CHECK被设置为必要的值,以使CMF * 256 + FLG是31的倍数。 - Kevin Newman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接