C# .NET Gzip流解压缩生成空流
我正在尝试序列化和压缩WPF,然后使用.NET类执行相反的操作—解压缩字节数组并反序列化以重新创建FlowDocument。我遵循MSDN上描述的示例,我有以下测试程序:C# .NET Gzip流解压缩生成空流,c#,.net,wpf,compression,gzipstream,C#,.net,Wpf,Compression,Gzipstream,我正在尝试序列化和压缩WPF,然后使用.NET类执行相反的操作—解压缩字节数组并反序列化以重新创建FlowDocument。我遵循MSDN上描述的示例,我有以下测试程序: var flowDocumentIn = new FlowDocument(); flowDocumentIn.Blocks.Add(new Paragraph(new Run("Hello"))); Debug.WriteLine("Compress"); byte[] compressedData; using (var
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
为什么最终的未压缩的流不包含原始的123字节
(请忽略“压缩”字节数组比“未压缩”字节数组大的事实-我通常会处理更大的流文档)在从内存流获取压缩字节之前,需要关闭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();
}
这是可行的,您甚至可以使用MemoryStream
删除,因为它将由GZipStream
处理,除非您使用构造函数重载来指定底层流应该保持打开状态。这意味着您正在对已处理的流调用ToArray
,但这是允许的,因为字节仍然可用,这使得处理内存流有点奇怪,但如果不这样做,FXCop会让您感到恼火。Joao的回答起到了作用。我复制了下面完整的工作示例。我在输出压缩数据.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代码>在调用<代码> XAMLReald.Load ./P>> P>在将解压缩后的字节复制到流中之后,需要将其位置设置为0,以便可以正确读取。尽管您可以解决此问题,但首先应该考虑是否使用该类。请参阅我的评论:“在从内存流获取压缩字节之前,需要关闭GZipStream
”—为什么?如果在关闭之前调用.ToArray()
和关闭之后调用.ToArray()
为什么会得到不同的字节数?除了输出是以块的形式写入之外,GZipStream
在压缩数据之前添加一个页眉,在压缩数据之后添加一个页脚。页脚只能在关闭流时添加。deflate压缩器(根据gzip格式的要求)以块的形式生成压缩输出。压缩器需要累积数据以建立块并在其发出之前生成统计信息。当您到达输入的末尾时,您需要告诉平减指数完成最后一个块并将其发送出去。另外,压缩数据大小超过未压缩数据大小的量是GZipStream中的几个错误之一。