2010-11-11 13 views
8

Quindi ecco uno strano. Ho questo metodo per prendere una stringa sgonfio codifica Base64 e restituire i dati originari:DeflateStream non decomprime i dati (la prima volta)

public static string Base64Decompress(string base64data) 
{ 
    byte[] b = Convert.FromBase64String(base64data); 
    using (var orig = new MemoryStream(b)) 
    { 
     using (var inflate = new MemoryStream()) 
     { 
      using (var ds = new DeflateStream(orig, CompressionMode.Decompress)) 
      { 
       ds.CopyTo(inflate); 
       return Encoding.ASCII.GetString(inflate.ToArray()); 
      } 
     } 
    } 
} 

Questo restituisce una stringa vuota se non aggiungo una seconda chiamata a ds.CopyTo(inflate). (WTF?)

... 
      using (var ds = new DeflateStream(orig, CompressionMode.Decompress)) 
      { 
       ds.CopyTo(inflate); 
       ds.CopyTo(inflate); 
       return Encoding.ASCII.GetString(inflate.ToArray()); 
      } 
    ... 

(Flush/Close/Dispose su ds non hanno effetto.)

Perché le DeflateStream copia 0 byte alla prima chiamata? Ho anche provato a fare il ciclo con Read(), ma restituisce anche zero alla prima chiamata, quindi funziona sul secondo.


Aggiornamento: ecco il metodo che sto usando per comprimere i dati.

public static string Base64Compress(string data, Encoding enc) 
{ 
    using (var ms = new MemoryStream()) 
    { 
     using (var ds = new DeflateStream(ms, CompressionMode.Compress)) 
     { 
      byte[] b = enc.GetBytes(data); 
      ds.Write(b, 0, b.Length); 
      ds.Flush(); 
      return Convert.ToBase64String(ms.ToArray()); 
     } 
    } 
} 
+0

Questo è molto interessante. Cosa succede quando sostituisci il primo dei due 'ds.CopyTo()' con un 'ds.Read (...)'? Il primo 'CopyTo()' si attiva leggendo sul piè di pagina del flusso. 'Leggi()' dovrebbe fare lo stesso. Mi stavo solo chiedendo. –

+0

Sei sicuro che sia deflate e non compresso con gzip? E sei sicuro che non ci siano altre cose di fronte ai dati deflate (o gzip?)? – nos

+0

@Pieter: un '.Read()' ha lo stesso effetto - restituisce '0', ma fa sì che la prossima chiamata a' CopyTo() 'funzioni. – josh3736

risposta

7

Ciò accade quando i byte compressi sono incompleti (cioè, non tutti i blocchi vengono scritte).

Se utilizzo Base64Compress con il seguente metodo di decompressione, otterrò InvalidDataException con il messaggio "Tipo di blocco sconosciuto". Lo stream potrebbe essere danneggiato. '

Decompress

public static string Decompress(Byte[] bytes) 
{ 
    using (var uncompressed = new MemoryStream()) 
    using (var compressed = new MemoryStream(bytes)) 
    using (var ds = new DeflateStream(compressed, CompressionMode.Decompress)) 
    { 
    ds.CopyTo(uncompressed); 
    return Encoding.ASCII.GetString(uncompressed.ToArray()); 
    } 
} 

Nota che tutto funziona come previsto quando si utilizza il seguente metodo Compress

public Byte[] Compress(Byte[] bytes) 
{ 
    using (var memoryStream = new MemoryStream()) 
    { 
    using (var deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress)) 
     deflateStream.Write(bytes, 0, bytes.Length); 

    return memoryStream.ToArray(); 
    } 
} 

Aggiornamento

Ops, stupida me ... non si può ToArray la memoria flusso finché non si disfa DeflateStream (poiché flush non è implementato acuitamente (e Deflate/GZip comprime i blocchi di ata); il blocco finale è solo scritto su close/dispose.

riscrivere comprimere come:

public static string Base64Compress(string data, Encoding enc) 
{ 
    using (var ms = new MemoryStream()) 
    { 
    using (var ds = new DeflateStream(ms, CompressionMode.Compress)) 
    { 
     byte[] b = enc.GetBytes(data); 
     ds.Write(b, 0, b.Length); 
    } 

    return Convert.ToBase64String(ms.ToArray()); 
    } 
} 
+0

Sì, questo è il problema.Tecnicamente dovresti usare il sovraccarico DeflateStream() che accetta l'argomento leaveOpen e passa * true *. Senza di esso, la chiusura/eliminazione di DeflateStream disporrà anche di MemoryStream. Che questo non causi un problema adesso è un incidente. –

+0

@Hans, sicuramente non è una cattiva idea, anche se Smaltire un MemoryStream non cancella il buffer; piuttosto impedisce solo ulteriori letture/scritture sul MemoryStream. Quindi tecnicamente c'è un doppio smaltimento su MemoryStream, i byte a cui si accede tramite ToArray sono comunque accessibili a prescindere. –

+0

Sì. Ho una grande quantità di cr * p in SO per aver sottolineato che smaltire un MemoryStream è sciocco. Felice di restituirne un po ':) –

Problemi correlati