.NET GZipStream распаковать производя пустой поток
Я пытаюсь сериализовать и сжать WPF FlowDocument
, а затем сделать обратное - распаковать массив байтов и десериализовать для воссоздания FlowDocument - используя класс .NET GZipStream
. Я следую примеру, описанному в MSDN, и у меня есть следующая тестовая программа:
var flowDocumentIn = new FlowDocument();
flowDocumentIn.Blocks.Add(new Paragraph(new Run("Hello")));
Debug.WriteLine("Compress");
byte[] compressedData;
using (var uncompressed = new MemoryStream())
{
XamlWriter.Save(flowDocumentIn, uncompressed);
uncompressed.Position = 0;
using (var compressed = new MemoryStream())
using (var compressor = new GZipStream(compressed, CompressionMode.Compress))
{
Debug.WriteLine(" uncompressed.Length: " + uncompressed.Length);
uncompressed.CopyTo(compressor);
Debug.WriteLine(" compressed.Length: " + compressed.Length);
compressedData = compressed.ToArray();
}
}
Debug.WriteLine("Decompress");
FlowDocument flowDocumentOut;
using (var compressed = new MemoryStream(compressedData))
using (var uncompressed = new MemoryStream())
using (var decompressor = new GZipStream(compressed, CompressionMode.Decompress))
{
Debug.WriteLine(" compressed.Length: " + compressed.Length);
decompressor.CopyTo(uncompressed);
Debug.WriteLine(" uncompressed.Length: " + uncompressed.Length);
flowDocumentOut = (FlowDocument) XamlReader.Load(uncompressed);
}
Assert.AreEqual(flowDocumentIn, flowDocumentOut);
Однако я получаю исключение в строке XamlReader.Load
, которая является нормальной, поскольку вывод отладки говорит о том, что несжатый поток имеет нулевую длину.
Compress
uncompressed.Length: 123
compressed.Length: 202
Decompress
compressed.Length: 202
uncompressed.Length: 0
Почему окончательный поток uncompressed
не содержит исходных 123 байта?
(Пожалуйста, игнорируйте тот факт, что "сжатый" массив байтов больше, чем "несжатый" массив байтов - я обычно буду работать с гораздо большими документами потока)
Ответы
Ответ 1
Вам нужно закрыть GZipStream
, прежде чем получать сжатые байты из потока памяти. В этом случае закрытие обрабатывается Dispose
, вызванным использованием.
using (var compressed = new MemoryStream())
{
using (var compressor = new GZipStream(compressed, CompressionMode.Compress))
{
uncompressed.CopyTo(compressor);
}
// Get the compressed bytes only after closing the GZipStream
compressedBytes = compressed.ToArray();
}
Это работает, и вы даже можете удалить using
для MemoryStream
, так как он будет удален GZipStream
, если вы не используете перегрузку конструктора, которая позволяет вам указать, что базовый поток должен быть оставлен открытым. Это подразумевает, что этот код вы вызываете ToArray
в удаленном потоке, но это разрешено, потому что байты все еще доступны, что делает распоряжения потоками памяти немного странными, но если вы этого не сделаете, FXCop будет вас раздражать.
Ответ 2
Ответ Жоаа сделал трюк. Я скопировал полный рабочий пример ниже. Я добавил строку для вывода compressedData.Length
. Интересно, что это выводит 218 байт, тогда как compressedStream.Length
выводит только 202 байта. Если вы не закрываете GZipStream перед чтением байтового массива, тогда compressedData.Length
- 202. Я не уверен, почему закрытие GZipStream дает дополнительные 16 байт.
var flowDocumentIn = new FlowDocument();
flowDocumentIn.Blocks.Add(new Paragraph(new Run("Hello")));
Debug.WriteLine("Compress");
byte[] compressedData;
using (var uncompressedStream = new MemoryStream())
{
XamlWriter.Save(flowDocumentIn, uncompressedStream);
uncompressedStream.Position = 0;
using (var compressedStream = new MemoryStream())
{
using (var gZipCompressor = new GZipStream(compressedStream, CompressionMode.Compress))
{
Debug.WriteLine(" uncompressedStream.Length: " + uncompressedStream.Length);
uncompressedStream.CopyTo(gZipCompressor);
Debug.WriteLine(" compressedStream.Length: " + compressedStream.Length);
}
compressedData = compressedStream.ToArray();
}
}
Debug.WriteLine(" compressedData.Length: " + compressedData.Length);
Debug.WriteLine("Decompress");
FlowDocument flowDocumentOut;
using (var compressedStream = new MemoryStream(compressedData))
using (var uncompressedStream = new MemoryStream())
{
using (var gZipDecompressor = new GZipStream(compressedStream, CompressionMode.Decompress))
{
Debug.WriteLine(" compressedStream.Length: " + compressedStream.Length);
gZipDecompressor.CopyTo(uncompressedStream);
Debug.WriteLine(" uncompressedStream.Length: " + uncompressedStream.Length);
}
uncompressedStream.Position = 0;
flowDocumentOut = (FlowDocument)XamlReader.Load(uncompressedStream);
}
Отладочный вывод:
Compress
uncompressedStream.Length: 123
compressedStream.Length: 202
compressedData.Length: 218
Decompress
compressedStream.Length: 218
uncompressedStream.Length: 123
Обратите внимание также на дополнительный uncompressedStream.Position = 0;
перед вызовом XamlReader.Load
.
Ответ 3
После копирования распакованных байтов в ваш поток вам нужно установить его положение в ноль, чтобы вы могли его правильно прочитать