2012-08-11 54 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

在複製解壓縮字節到信息流,你需要它的位置設置爲零,這樣就可以讀取它正確