2009-04-18 17 views
2

我正在研究my other question的解決方案,該解決方案正在讀取PNG的'zTXt'塊中的數據。我儘可能在文件中查找塊,並閱讀zTXt的關鍵字。我無法閱讀zTXt的壓縮部分。我從來沒有使用過DeflateStream對象,並且遇到了一些麻煩。在讀取時,它似乎期望長度參數處於「未壓縮」字節。然而,就我而言,我只知道'壓縮'字節中數據的長度。爲了有希望解決這個問題,我把需要解壓縮的所有數據放到MemoryStream中,然後用DeflateStream「讀取並結束」。現在,這只是一個很好的例外,除了它會拋出一個InvalidDataException,並顯示消息「塊長度與其補碼不匹配」。現在我不知道這是什麼意思。可能會出現什麼問題?如何在文件的一部分上使用DeflateStream?

對於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是如何工作的。

非常感謝!

回答

5

畢竟這一次,我終於找到了問題。數據採用zlib格式,與僅使用DEFLATE相比,存儲的數據多一點。如果我在獲取壓縮數據之前剛剛讀取了2個額外的字節,則該文件會被正確讀取。

請參閱this feedback page。 (我沒有提交那個。)

我想知道現在。這兩個字節的值分別是0x78和0x9C。如果我發現其他值,我應該假設DEFLATE會失敗?

+3

http://www.faqs.org/rfcs/rfc1950.html描述了ZLib格式。第一個字節0x78稱爲CMF,值表示CM = 8,CINFO = 7。CM = 8表示窗口大小高達32K的「放氣」壓縮方法。這是gzip和PNG使用的方法。 CINFO = 7表示32K窗口大小。 0x9C被稱爲FLG,值表示FLEVEL = 2,CHECK = 28。解壓縮時不需要FLEVEL中的信息;它在那裏表明再壓縮是否值得。 CHECK被設置爲任何需要的值,使得CMF * 256 + FLG是31的倍數。 – 2009-05-28 10:13:30

相關問題