2009-10-06 3 views
4

У меня возникли проблемы с чтением сжатого (дефлированного) файла данных с использованием C# .NET DeflateStream(..., CompressionMode.Decompress). Файл был написан ранее с использованием DeflateStream(..., CompressionMode.Compress), и это кажется очень хорошим (я могу даже распаковать его с помощью программы Java).Uncompress data file with DeflateStream

Однако, первый вызов Read() на входной поток для распаковки/раздувания сжатых данных возвращает длину нуля (конец файла).

Вот главный драйвер, который используется как для сжатия и распаковки:

public void Main(...) 
{ 
    Stream inp; 
    Stream outp; 
    bool compr; 

    ... 
    inp = new FileStream(inName, FileMode.Open, FileAccess.Read); 
    outp = new FileStream(outName, FileMode.Create, FileAccess.Write); 

    if (compr) 
     Compress(inp, outp); 
    else 
     Decompress(inp, outp); 

    inp.Close(); 
    outp.Close(); 
} 

Вот основной код для декомпрессии, что и не удается:

public long Decompress(Stream inp, Stream outp) 
{ 
    byte[] buf = new byte[BUF_SIZE]; 
    long nBytes = 0; 

    // Decompress the contents of the input file 
    inp = new DeflateStream(inp, CompressionMode.Decompress); 

    for (;;) 
    { 
     int len; 

     // Read a data block from the input stream 
     len = inp.Read(buf, 0, buf.Length); //<<FAILS 
     if (len <= 0) 
      break; 

     // Write the data block to the decompressed output stream 
     outp.Write(buf, 0, len); 
     nBytes += len; 
    } 

    // Done 
    outp.Flush(); 
    return nBytes; 
} 

Вызов отмечен FAILS всегда возвращает ноль. Зачем? Я знаю, что это должно быть что-то простое, но я просто этого не вижу.

Вот основной код сжатия, который работает просто отлично, и почти точно так же, как метод декомпрессии с именами поменялись местами:

public long Compress(Stream inp, Stream outp) 
{ 
    byte[] buf = new byte[BUF_SIZE]; 
    long nBytes = 0; 

    // Compress the contents of the input file 
    outp = new DeflateStream(outp, CompressionMode.Compress); 

    for (;;) 
    { 
     int len; 

     // Read a data block from the input stream 
     len = inp.Read(buf, 0, buf.Length); 
     if (len <= 0) 
      break; 

     // Write the data block to the compressed output stream 
     outp.Write(buf, 0, len); 
     nBytes += len; 
    } 

    // Done 
    outp.Flush(); 
    return nBytes; 
} 

решаемые

Осмотрев правильное решение , оператор конструктора должен быть изменен на:

inp = new DeflateStream(inp, CompressionMode.Decompress, true); 

, который поддерживает базовый поток ввода открытая, и следующая строка должна быть добавлена ​​после inp.Flush() вызова:

inp.Close(); 

В Close() вызов заставляет поток Deflater, чтобы промыть внутренние буфера. Флаг true предотвращает его закрытие базового потока, который закрывается позже в Main(). Те же изменения должны быть внесены и в метод Compress().

ответ

4

В вашем методе распаковки переназначаем inp для нового потока (поток спускания). Вы никогда не закрываете этот поток Deflate, но вы закрываете основной поток файлов в Main(). Аналогичная вещь происходит в методе сжатия.

Я думаю, что проблема в том, что основной поток файлов закрывается до того, как финализаторы потока дефлятирования автоматически закрывают их.

Я добавил 1 строку кода в ваших разжимать и сжимать методы: inp.Close() // в Decompressmehtod

outp.Close() // к методу компресса.

лучшей практикой было бы включение потоков в предложение использования.

Вот альтернативный способ написать свой метод Decompress (я проверил, и она работает)

 

    public static long Decompress(Stream inp, Stream outp) 
    { 
     byte[] buf = new byte[BUF_SIZE]; 
     long nBytes = 0; 

     // Decompress the contents of the input file 
     using (inp = new DeflateStream(inp, CompressionMode.Decompress)) 
     { 
      int len; 
      while ((len = inp.Read(buf, 0, buf.Length)) > 0) 
      { 
       // Write the data block to the decompressed output stream 
       outp.Write(buf, 0, len); 
       nBytes += len; 
      } 
     } 
     // Done 
     return nBytes; 
    } 
 
+0

@LoadMaster: КСТАТИ: Когда я побежал ваш код, я не получил такое же поведение вы описали, но это тоже не сработало. В моем случае распаковка оказалась успешной, но если бы я сравнил декомпрессированный файл с оригиналом, они не совпадали. Декомпрессированный файл был меньше исходного - отсутствующие байты. Изменение, которое мы называем close() на DeflateStream, также исправило эту проблему. Я подозреваю, что разница в поведении может быть связана только с размером моего тестового файла с вашим - возможно, он потерял последние N байтов, а мой файл был больше (мой был 364,391 байт). – JMarsch

+0

Дох. Конечно, в документах говорится, что вам нужно закрыть поток дефлатера, чтобы убедиться, что все внутренние буферы правильно очищены. Я должен вызывать 'Close()' в потоках компрессоров/декомпрессоров в методах 'Decompress()' и 'Compress()', поскольку они напрямую управляют потоками компрессора/декомпрессора. В качестве альтернативы я мог бы открыть 'DeflaterStream' с флагом' keepOpen' true, чтобы открыть базовый входной поток. Я все равно закрою поток дефлатера, но потом я также закрою основной поток файлов. Я пробовал это, и он работает. –

0

У меня была такая же проблема с GZipStream, так как у нас была оригинальная длина, мне пришлось перезаписать код, чтобы читать только количество байтов, ожидаемых в исходном файле.

Надеюсь, я собираюсь узнать, что был лучший ответ (скрещенные пальцы).