c# - GZipStream y DeflateStream no descomprimirán todos los bytes
image compression (2)
Necesitaba una forma de comprimir imágenes en .net, así que busqué usar la clase .net GZipStream (o DeflateStream). Sin embargo, descubrí que la descompresión no siempre era exitosa, a veces las imágenes se descomprimían bien y otras veces obtenía un error GDI + de que algo estaba dañado.
Después de investigar el problema, descubrí que la descompresión no devolvía todos los bytes comprimidos. Entonces, si comprimía 2257974 bytes, a veces obtengo solo 2257870 bytes (números reales).
Lo más gracioso es que a veces funcionaría. Así que creé este pequeño método de prueba que comprime solo 10 bytes y ahora no recupero nada.
Lo intenté con ambas clases de compresión, GZipStream y DeflateStream, y revisé mi código para ver si había posibles errores. Incluso intenté posicionar la transmisión en 0 y lavar todas las transmisiones, pero sin suerte.
Aquí está mi código:
public static void TestCompression()
{
byte[] test = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
byte[] result = Decompress(Compress(test));
// This will fail, result.Length is 0
Debug.Assert(result.Length == test.Length);
}
public static byte[] Compress(byte[] data)
{
var compressedStream = new MemoryStream();
var zipStream = new GZipStream(compressedStream, CompressionMode.Compress);
zipStream.Write(data, 0, data.Length);
return compressedStream.ToArray();
}
public static byte[] Decompress(byte[] data)
{
var compressedStream = new MemoryStream(data);
var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress);
var resultStream = new MemoryStream();
var buffer = new byte[4096];
int read;
while ((read = zipStream.Read(buffer, 0, buffer.Length)) > 0) {
resultStream.Write(buffer, 0, read);
}
return resultStream.ToArray();
}
ZipStream
Close()
el ZipStream
después de agregar todos los datos que desea comprimir; retiene un búfer de bytes no escritos internamente (incluso si Flush()
) que debe escribirse.
De manera más general, Stream
es IDisposable
, por lo que también debería using
cada ... (sí, sé que MemoryStream
no va a perder ningún dato, pero si no adquiere este hábito, lo morderá con otros Stream
s).
public static byte[] Compress(byte[] data)
{
using (var compressedStream = new MemoryStream())
using (var zipStream = new GZipStream(compressedStream, CompressionMode.Compress))
{
zipStream.Write(data, 0, data.Length);
zipStream.Close();
return compressedStream.ToArray();
}
}
public static byte[] Decompress(byte[] data)
{
using(var compressedStream = new MemoryStream(data))
using(var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress))
using (var resultStream = new MemoryStream())
{ ... }
}
[edit: comentario actualizado] No estoy using
cosas como MemoryStream
- esto siempre es divertido, con muchos votos en cada lado de la valla: pero al final ...
(Retórica, todos sabemos la respuesta ...) ¿Cómo se implementa MemoryStream
? ¿es un byte [] (propiedad de .NET)? ¿es un archivo mapeado en memoria (propiedad del sistema operativo)?
La razón por la que no la está using
es porque está dejando que el conocimiento de los detalles de la implementación interna cambie la forma en que codifica en contra de una API pública, es decir, acaba de violar las leyes de encapsulación. La API pública dice: soy IDisposable
; usted "me posee" a mí; por lo tanto, es su trabajo Dispose()
cuando haya terminado.
Además, tenga en cuenta que DeflateStream en System.IO.Compression no implementa el algoritmo de desinflado más eficiente. Si lo desea, existe una alternativa al BCL GZipStream y DeflateStream; se implementa en una biblioteca totalmente administrada basada en el código zlib, que funciona mejor que el flujo integrado {Deflate, GZip} a este respecto. [Pero aún necesita Cerrar () la transmisión para obtener la corriente de detección completa. ]
Estas clases de flujo se envían en el ensamblaje DotNetZlib, disponible en la distribución DotNetZip en http://DotNetZip.codeplex.com/ .