Domanda

Avevo bisogno di un modo per comprimere le immagini in .net, quindi ho cercato di utilizzare la classe .net GZipStream (o DeflateStream). Tuttavia ho scoperto che la decompressione non ha sempre avuto successo, a volte le immagini si decomprimono bene e altre volte ricevo un errore GDI + che qualcosa è danneggiato.

Dopo aver esaminato il problema, ho scoperto che la decompressione non stava restituendo tutti i byte compressi. Quindi, se comprimessi 2257974 byte, a volte ritornerei solo 2257870 byte (numeri reali).

La cosa più divertente è che a volte funzionerebbe. Quindi ho creato questo piccolo metodo di test che comprime solo 10 byte e ora non riesco a recuperare nulla.

L'ho provato con entrambe le classi di compressione GZipStream e DeflateStream e ho ricontrollato il mio codice per possibili errori. Ho anche provato a posizionare lo stream su 0 e a svuotare tutti i flussi, ma senza fortuna.

Ecco il mio codice:

    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();
    }
È stato utile?

Soluzione

Devi Chiudi () il ZipStream dopo aver aggiunto tutti i dati che vuoi comprimere; mantiene internamente un buffer di byte non scritti (anche se Flush () ) che deve essere scritto.

Più in generale, Stream è IDisposable , quindi dovresti anche usare ciascuno ... (sì, lo so che MemoryStream non perderà alcun dato, ma se non prendi questa abitudine, ti morderà con altri Stream .

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())
    { ... }
}

[modifica: aggiornamento del nuovo commento] Non usare cose come MemoryStream - questo è sempre divertente, con un sacco di voti su entrambi i lati della barriera: ma alla fine ...

(retorico: conosciamo tutti la risposta ...) Come viene implementato MemoryStream ? è un byte [] (di proprietà di .NET)? è un file mappato in memoria (di proprietà del sistema operativo)?

Il motivo per cui non stai usando è perché stai facendo sapere ai dettagli dell'implementazione interna cambiare il modo in cui codifichi contro un'API pubblica - cioè hai appena infranto le leggi dell'incapsulamento. L'API pubblica dice: I am IDisposable ; tu "possiedi" me; pertanto, il tuo compito è Dispose () quando hai finito.

Altri suggerimenti

Inoltre, tieni presente che DeflateStream in System.IO.Compression non implementa l'algoritmo di deflate più efficiente. Se ti piace, c'è un'alternativa a BCL GZipStream e DeflateStream; è implementato in una libreria completamente gestita basata sul codice zlib, che offre prestazioni migliori rispetto allo stream {Deflate, GZip} incorporato in questo senso. [Ma devi ancora chiudere () il flusso per ottenere il bytestream completo. ]

Queste classi di stream vengono spedite nell'assieme DotNetZlib, disponibile nella distribuzione DotNetZip all'indirizzo http: //DotNetZip.codeplex. com / .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top