2012-08-11 9 views
10

Próbuję serializować i kompresować WPF FlowDocument, a następnie zrobić odwrotność - rozpakować tablicę bajtów i deserializować w celu odtworzenia FlowDocument - przy użyciu klasy .NET GZipStream. Obserwuję przykład opisany na MSDN i mam następujący program testowy:.NET GZipStream dekompresuje produkcję pustego strumienia

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); 

jednak uzyskać wyjątek w XamlReader.Load linii co jest normalne, ponieważ wyjście debugowania mówi, że nieskompresowany strumień ma długość zerową.

Compress 
uncompressed.Length: 123 
compressed.Length: 202 
Decompress 
compressed.Length: 202 
uncompressed.Length: 0 

Dlaczego nie ostateczna uncompressed strumień zawiera oryginalne 123 bajtów?

(proszę ignorować faktu, że „skompresowany” Tablica bajtów jest większa niż „nieskompresowanego” tablicy bajtów - będę normalnie pracować ze znacznie większymi dokumentami Flow)

+0

Choć może rozwiązać ten problem, należy rozważyć, czy chcesz korzystać z tej klasy w pierwsze miejsce. Zobacz moje komentarze tutaj: http://stackoverflow.com/questions/11435200/why-does-my-c-sharp-gzip-produce-a-larger-file-than-fiddler-or-php –

Odpowiedz

11

Trzeba zamknąć GZipStream przed uzyskaniem skompresowane bajty ze strumienia pamięci. W takim przypadku zamknięcie jest obsługiwane przez wywoływane Dispose ze względu na użycie.

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(); 
} 

To działa i można nawet usunąć using dla MemoryStream ponieważ zostanie on umieszczony przez GZipStream chyba użyć przeciążenie konstruktora, który pozwala określić, że strumień bazowy należy pozostawić otwarte. Wynika to z tego kodu, który wywołujemy ToArray na wysyłanym strumieniu, ale jest to dozwolone, ponieważ bajty są nadal dostępne, co sprawia, że ​​wydzielanie strumieni pamięci jest trochę dziwne, ale jeśli tego nie zrobisz, FXCop będzie cię irytować.

+0

"Musisz zamknąć 'GZipStream' przed pobraniem skompresowanych bajtów ze strumienia pamięci" - dlaczego? I dlaczego otrzymujesz inną liczbę bajtów, jeśli wywołasz '.ToArray()' przed lub po zamknięciu? –

+2

Oprócz faktu, że dane wyjściowe są zapisywane w blokach, 'GZipStream' dodaje nagłówek przed skompresowanymi danymi i stopką po. Stopkę można dodać tylko w momencie zamknięcia strumienia. –

3

Joao odpowiedział. Skopiowałem pełny przykład roboczy poniżej. Dodałem linię do wyjścia compressedData.Length. Co ciekawe, generuje 218 bajtów, podczas gdy compressedStream.Length wysyła tylko 202 bajty. Jeśli nie zamknąć GZipStream przed zapoznaniem się z tablicy bajtów następnie compressedData.Length jest 202. Nie jestem pewien, dlaczego zamknięcie GZipStream daje dodatkowe 16 bajtów ..

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); 
} 

wyjście Debug:

Compress 
uncompressedStream.Length: 123 
compressedStream.Length: 202 
compressedData.Length: 218 
Decompress 
compressedStream.Length: 218 
uncompressedStream.Length: 123 

Należy również zanotować dodatkową wiadomość uncompressedStream.Position = 0; przed połączeniem z numerem XamlReader.Load.

+2

Sprężarka deflacyjna (wymagana dla formatu gzip) wytwarza skompresowane wyjście w blokach. Kompresor musi gromadzić dane, aby zbudować blok i wygenerować statystyki na nim przed emisją. Gdy dojdziesz do końca danych wejściowych, musisz powiedzieć deflatorowi, aby dokończył ostatni blok i wysłał go. W przeciwnym razie dane będą po prostu czekały, aż więcej danych wypełni blok. –

+1

Nawiasem mówiąc, wielkość, o którą rozmiar skompresowanych danych przekracza nieskompresowany rozmiar danych, jest jednym z kilku błędów w GZipStream. –

0

Po skopiowaniu po dekompresji bajtów w strumieniu, trzeba ustawić swoją pozycję na zero, dzięki czemu można go odczytać prawidłowo

Powiązane problemy