Как вы используете DeflateStream на части файла? - PullRequest
3 голосов
/ 18 апреля 2009

Я работаю над решением моего другого вопроса , который читает данные в виде фрагментов 'zTXt' PNG. Я дошел до того, чтобы найти куски в файле и прочитать ключевое слово zTXt. У меня проблемы с чтением сжатой части zTXt. Я никогда раньше не работал с объектом DeflateStream, и у меня возникли некоторые проблемы с ним. При чтении кажется, что параметр длины находится в «несжатых» байтах. В моем случае, однако, я знаю только длину данных в «сжатых» байтах. Чтобы, надеюсь, обойти это, я поместил все данные, которые нужно было распаковать, в MemoryStream, а затем «прочитал до конца» с помощью DeflateStream. Теперь это просто замечательно, за исключением того, что оно создает исключение InvalidDataException с сообщением «Длина блока не совпадает с его дополнением». Теперь я понятия не имею, что это значит. Что может быть не так?

Формат чанка составляет 4 байта для идентификатора ("zTXt"), 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 голосов
/ 22 мая 2009

После всего этого времени я наконец нашел проблему. Данные представлены в формате zlib, в котором хранится немного больше данных, чем при использовании только DEFLATE. Файл читается правильно, если я просто прочитал 2 дополнительных байта прямо перед тем, как получить сжатые данные.

См. эту страницу обратной связи . (Я не представил это.)

Мне интересно сейчас. Значения этих двух байтов равны 0x78 и 0x9C соответственно. Если я найду значения, отличные от этих, я должен предположить, что DEFLATE потерпит неудачу?

...