2012-08-11 99 views
10

我试图序列化和压缩一个WPF FlowDocument,然后做相反的事情 - 解压字节数组并反序列化以重新创建FlowDocument - 使用.NET类GZipStream。我以下MSDN上所描述的示例和我有以下的测试程序:.NET GZipStream解压缩生成空流

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字节?

(请忽略的事实是,“压缩”字节数组是比“未压缩的”字节数组大 - 我通常会用更大的流文件的工作)

+0

虽然你可能解决这个问题,你应该考虑是否要使用这个类的第一名。在这里看到我的意见:http://stackoverflow.com/questions/11435200/why-does-my-c-sharp-gzip-produce-a-larger-file-than-fiddler-or-php – 2012-08-11 20:03:42

回答

11

你需要让之前关闭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(); 
} 

这工作,你甚至可以删除usingMemoryStream,因为它会被GZipStream除非你用构造函数重载,允许您指定基础流应保持开路来布置。这意味着你在代码中调用ToArray,但这是允许的,因为字节仍然可用,这使得处理内存流有点奇怪,但如果你不这样做,FXCop会激怒你。

+0

“您需要关闭在从内存流中获取压缩字节之前的'GZipStream'“ - 为什么?如果你在关闭之前调用'.ToArray()',为什么会得到不同的字节数? – 2012-08-11 21:03:57

+2

由于输出是以块的形式写入,'GZipStream'在压缩数据和页脚之后添加了一个头。只有在关闭流的时刻才能添加页脚。 – 2012-08-11 21:53:10

3

若昂的答案做到了。我已经复制下面的完整工作示例。我添加了一行输出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 

还请注意在致电XamlReader.Load之前的附加uncompressedStream.Position = 0;

+2

放气压缩机(按照gzip格式的要求)以块形式生成压缩输出。压缩机需要积累数据才能建立一个模块并在发射前生成统计数据。当你的输入结束时,你需要告诉平滑器完成最后一个块并发送出去。否则,这些数据就会坐在那里等待更多的数据填满一个块。 – 2012-08-11 21:12:35

+1

顺便说一句,压缩数据大小超过未压缩的数据大小是GZipStream中的几个错误之一。 – 2012-08-11 21:13:18

0

在复制解压缩字节到信息流,你需要它的位置设置为零,这样就可以读取它正确