Можно ли использовать DeflateStream или GZipStream для сжатия несжатого файла? - PullRequest
3 голосов
/ 09 сентября 2010

Я пытаюсь реализовать сжатие файлов в приложении.Приложение существует уже некоторое время, поэтому оно должно быть в состоянии читать несжатые документы, написанные в предыдущих версиях.Я ожидал, что DeflateStream сможет обработать несжатый файл, но для GZipStream я получаю ошибку «Магическое число в заголовке GZip неверно».Для DeflateStream я получаю «Обнаружены неверные данные при декодировании».Я думаю, что он не находит заголовок, который помечает файл как его тип.

Если невозможно просто обработать несжатый файл, то 2-й лучше всего будет иметь способ определить, является ли файлсжатый, и выберите метод чтения файла.Я нашел эту ссылку: http://blog.somecreativity.com/2008/04/08/how-to-check-if-a-file-is-compressed-in-c/,, но это очень зависит от реализации и не похоже на правильный подход.Он также может давать ложные срабатывания (я уверен, что это будет редко, но это указывает на то, что это неправильный подход).

Третий вариант, который я рассмотрел, - попытаться использовать DeflateStream и использоватьнормальный поток ввода-вывода, если возникает исключение.Это также кажется беспорядочным и приводит к сбою VS в исключении (если я не уберу галочку с этого исключения, которое я не хочу делать).

Конечно, я могу простоНеправильный путь.Это код, который я пробовал в .Net 3.5:

Stream reader = new FileStream(fileName, FileMode.Open, readOnly ? FileAccess.Read : FileAccess.ReadWrite, readOnly ? FileShare.ReadWrite : FileShare.Read);

using (DeflateStream decompressedStream = new DeflateStream(reader, CompressionMode.Decompress))
{
    workspace = (Workspace)new XmlSerializer(typeof(Workspace)).Deserialize(decompressedStream);

    if (readOnly)
    {
        reader.Close();
        workspace.FilePath = fileName;
    }
    else
        workspace.SetOpen(reader, fileName);
}

Есть идеи?

Спасибо!Люк.

Ответы [ 2 ]

1 голос
/ 09 сентября 2010

Разве вы не можете просто создать класс / функцию-обертку для чтения файла и перехватить исключение?Что-то вроде

try
{
    // Try return decompressed stream 
}
catch(InvalidDataException e)
{
    // Assume it is already decompressed and return it as it is
}
1 голос
/ 09 сентября 2010

Разве у вашего формата файла нет заголовка?Если нет, то сейчас - это время для его добавления (в любом случае вы меняете формат файла, поддерживая сжатие).Выберите хорошее магическое значение , убедитесь, что заголовок расширяемый (добавьте поле версии или используйте специальные магические значения для определенных версий), и вы готовы к работе.

После загрузкипроверьте магическое значение.Если нет, используйте ваши текущие стандартные процедуры загрузки.Если он присутствует, заголовок скажет вам, сжато ли содержимое.

Обновить

Сжатие потока означает, что файл больше не является документом XML, и, таким образом,нет особых оснований ожидать, что файл не может содержать больше, чем ваши данные stream .Вы действительно хотите заголовок, идентифицирующий ваш файл:)

Ниже приведен пример (псевдо) -кода;Я не знаю, если .net имеет «подпоток», SubRangeStream, скорее всего, вам придется самому кодировать (DeflateStream, вероятно, добавляет свой собственный заголовок, поэтому подпоток может не понадобиться; может оказаться полезным в дальнейшем,хотя).

Int64 oldPosition = reader.Position;
reader.Read(magic, 0, magic.length);
if(IsRightMagicValue(magic))
{
    Header header = ReadHeader(reader);
    Stream furtherReader = new SubRangeStream(reader, reader.Position, header.ContentLength); 
    if(header.IsCompressed)
    {
        furtherReader = new DeflateStream(furtherReader, CompressionMode.Decompress);
    }

    XmlSerializer xml = new XmlSerializer(typeof(Workspace));
    workspace = (Workspace) xml.Deserialize(furtherReader); 
} else
{
    reader.Position = oldPosition;
    LegacyLoad(reader);
}

В реальной жизни я бы поступил немного иначе - например, путем некоторой правильной обработки ошибок и их очистки.Кроме того, у меня не было бы нового кода загрузчика непосредственно в блоке IsRightMagicValue, а скорее я бы выделил работу либо на основе магического значения (одно магическое значение на версию файла), либо я бы оставил «общий»заголовок "порция с полями, общими для всех версий.Для обоих я бы использовал Factory Method , чтобы вернуть IWorkspaceReader в зависимости от версии файла.

...