У меня возникли проблемы с чтением сжатого (дефлированного) файла данных с использованием 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()
.
@LoadMaster: КСТАТИ: Когда я побежал ваш код, я не получил такое же поведение вы описали, но это тоже не сработало. В моем случае распаковка оказалась успешной, но если бы я сравнил декомпрессированный файл с оригиналом, они не совпадали. Декомпрессированный файл был меньше исходного - отсутствующие байты. Изменение, которое мы называем close() на DeflateStream, также исправило эту проблему. Я подозреваю, что разница в поведении может быть связана только с размером моего тестового файла с вашим - возможно, он потерял последние N байтов, а мой файл был больше (мой был 364,391 байт). – JMarsch
Дох. Конечно, в документах говорится, что вам нужно закрыть поток дефлатера, чтобы убедиться, что все внутренние буферы правильно очищены. Я должен вызывать 'Close()' в потоках компрессоров/декомпрессоров в методах 'Decompress()' и 'Compress()', поскольку они напрямую управляют потоками компрессора/декомпрессора. В качестве альтернативы я мог бы открыть 'DeflaterStream' с флагом' keepOpen' true, чтобы открыть базовый входной поток. Я все равно закрою поток дефлатера, но потом я также закрою основной поток файлов. Я пробовал это, и он работает. –