我正在研究my other question的解决方案,该解决方案正在读取PNG的“ zTXt”块中的数据。我尽可能地找到文件中的块,并阅读zTXt的关键字。我在读取zTXt的压缩部分时遇到了麻烦。我以前从未使用过DeflateStream对象,并且遇到了一些麻烦。读取时,似乎期望length参数位于“未压缩”字节中。但就我而言,我只知道“压缩”字节中的数据长度。为了解决这个问题,我将所有需要解压缩的数据放入MemoryStream中,然后以DeflateStream“读取到结尾”。现在,它只是一个桃子,只不过它抛出一个InvalidDataException消息,消息为“块长度与其补码不匹配”。现在我不知道这意味着什么。可能出什么问题了?
块的格式是ID(“ zTXt”)的4个字节,数据长度,数据的大端32位int,最后是我现在忽略的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的工作原理。
非常感谢!!
最佳答案
经过所有这些时间,我终于找到了问题所在。数据为zlib格式,与仅使用DEFLATE相比,它存储的数据更多。如果我在获得压缩数据之前就读了另外2个字节,则可以正确读取文件。
请参见this feedback page。 (我没有提交那个。)
我现在想知道。这两个字节的值分别为0x78和0x9C。如果我发现其他值之外的值,是否应该假设DEFLATE会失败?