web-dev-qa-db-fra.com

Comment utiliser GZipStream avec System.IO.MemoryStream?

J'ai un problème avec cette fonction de test où je prends une chaîne en mémoire, la compresse et la décompresse. La compression fonctionne très bien, mais je ne parviens pas à faire fonctionner la décompression.

//Compress
System.IO.MemoryStream outStream = new System.IO.MemoryStream();                
GZipStream tinyStream = new GZipStream(outStream, CompressionMode.Compress);
mStream.Position = 0;
mStream.CopyTo(tinyStream);

//Decompress    
outStream.Position = 0;
GZipStream bigStream = new GZipStream(outStream, CompressionMode.Decompress);
System.IO.MemoryStream bigStreamOut = new System.IO.MemoryStream();
bigStream.CopyTo(bigStreamOut);

//Results:
//bigStreamOut.Length == 0
//outStream.Position == the end of the stream.

Je pense que bigStream out devrait au moins contenir des données, surtout si mon flux source (outStream) est en cours de lecture. est-ce un bug de MSFT ou le mien?

39
random65537

Ce qui se passe dans votre code, c'est que vous continuez à ouvrir des flux, mais vous ne les fermez jamais.

  • À la ligne 2, vous créez une GZipStream. Ce flux n'écrira rien dans le flux sous-jacent tant qu'il n'aura pas l'impression que c'est le bon moment. Vous pouvez le dire en le fermant.

  • Toutefois, si vous le fermez, le flux sous-jacent (outStream) sera également fermé. Par conséquent, vous ne pouvez pas utiliser mStream.Position = 0 dessus.

Vous devez toujours utiliser using pour vous assurer que tous vos flux sont fermés. Voici une variation de votre code qui fonctionne.

var inputString = "“ ... ”";
byte[] compressed;
string output;

using (var outStream = new MemoryStream())
{
    using (var tinyStream = new GZipStream(outStream, CompressionMode.Compress))
    using (var mStream = new MemoryStream(Encoding.UTF8.GetBytes(inputString)))
        mStream.CopyTo(tinyStream);

    compressed = outStream.ToArray();
}

// “compressed” now contains the compressed string.
// Also, all the streams are closed and the above is a self-contained operation.

using (var inStream = new MemoryStream(compressed))
using (var bigStream = new GZipStream(inStream, CompressionMode.Decompress))
using (var bigStreamOut = new MemoryStream())
{
    bigStream.CopyTo(bigStreamOut);
    output = Encoding.UTF8.GetString(bigStreamOut.ToArray());
}

// “output” now contains the uncompressed string.
Console.WriteLine(output);
91
Timwi

Il s'agit d'un problème connu: http://blogs.msdn.com/b/bclteam/archive/2006/05/10/592551.aspx

J'ai un peu changé votre code pour que celui-ci fonctionne:

var mStream = new MemoryStream(new byte[100]);
var outStream = new System.IO.MemoryStream();

using (var tinyStream = new GZipStream(outStream, CompressionMode.Compress))
{
    mStream.CopyTo(tinyStream);           
}

byte[] bb = outStream.ToArray();

//Decompress                
var bigStream = new GZipStream(new MemoryStream(bb), CompressionMode.Decompress);
var bigStreamOut = new System.IO.MemoryStream();
bigStream.CopyTo(bigStreamOut);
32
Aliostad

Une autre implémentation, dans VB.NET:

Imports System.Runtime.CompilerServices
Imports System.IO
Imports System.IO.Compression

Public Module Compressor

    <Extension()> _
    Function CompressASCII(str As String) As Byte()

        Dim bytes As Byte() = Encoding.ASCII.GetBytes(str)

        Using ms As New MemoryStream

            Using gzStream As New GZipStream(ms, CompressionMode.Compress)

                gzStream.Write(bytes, 0, bytes.Length)

            End Using

            Return ms.ToArray

        End Using

    End Function

    <Extension()> _
    Function DecompressASCII(compressedString As Byte()) As String

        Using ms As New MemoryStream(compressedString)

            Using gzStream As New GZipStream(ms, CompressionMode.Decompress)

                Using sr As New StreamReader(gzStream, Encoding.ASCII)

                    Return sr.ReadToEnd

                End Using

            End Using

        End Using

    End Function

    Sub TestCompression()

        Dim input As String = "fh3o047gh"

        Dim compressed As Byte() = input.CompressASCII()

        Dim decompressed As String = compressed.DecompressASCII()

        If input <> decompressed Then
            Throw New ApplicationException("failure!")
        End If

    End Sub

End Module
5
Nikolai Koudelia

La manière de compresser et de décompresser vers et depuis une MemoryStream est la suivante:

public static Stream Compress(
    Stream decompressed, 
    CompressionLevel compressionLevel = CompressionLevel.Fastest)
{
    var compressed = new MemoryStream();
    using (var Zip = new GZipStream(compressed, compressionLevel, true))
    {
        decompressed.CopyTo(Zip);
    }

    compressed.Seek(0, SeekOrigin.Begin);
    return compressed;
}

public static Stream Decompress(Stream compressed)
{
    var decompressed = new MemoryStream();
    using (var Zip = new GZipStream(compressed, CompressionMode.Decompress, true))
    {
        Zip.CopyTo(decompressed);
    }

    decompressed.Seek(0, SeekOrigin.Begin);
    return decompressed;
}

Cela laisse le flux compressé/décompressé ouvert et, en tant que tel, utilisable après sa création.

2
briantyler
    public static byte[] compress(byte[] data)
    {
        using (MemoryStream outStream = new MemoryStream())
        {
            using (GZipStream gzipStream = new GZipStream(outStream, CompressionMode.Compress))
            using (MemoryStream srcStream = new MemoryStream(data))
                srcStream.CopyTo(gzipStream);
            return outStream.ToArray();
        }
    }

    public static byte[] decompress(byte[] compressed)
    {
        using (MemoryStream inStream = new MemoryStream(compressed))
        using (GZipStream gzipStream = new GZipStream(inStream, CompressionMode.Decompress))
        using (MemoryStream outStream = new MemoryStream())
        {
            gzipStream.CopyTo(outStream);
            return outStream.ToArray();
        }
    }
1
PhonPanom

Si vous essayez d'utiliser MemoryStream (par exemple, en le passant dans une autre fonction) mais en recevant l'exception "Impossible d'accéder à un flux fermé". Ensuite, il existe un autre constructeur GZipStream que vous pouvez utiliser pour vous aider.

En passant un paramètre true au paramètre leaveOpen, vous pouvez indiquer à GZipStream de laisser le flux ouvert après s'être débarrassé de lui-même. Par défaut, le flux cible est fermé (ce à quoi je ne m'attendais pas). https://msdn.Microsoft.com/en-us/library/27ck2z1y(v=vs.110).aspx

using (FileStream fs = File.OpenRead(f))
using (var compressed = new MemoryStream())
{
    //Instruct GZipStream to leave the stream open after performing the compression.
    using (var gzipstream = new GZipStream(compressed, CompressionLevel.Optimal, true))
        fs.CopyTo(gzipstream);

    //Do something with the memorystream
    compressed.Seek(0, SeekOrigin.Begin);
    MyFunction(compressed);
}
1
Snives

Si vous en avez toujours besoin, vous pouvez utiliser le constructeur GZipStream avec un argument booléen (il en existe deux) et y transmettre la valeur vraie:

tinyStream = new GZipStream(outStream, CompressionMode.Compress, true);

Dans ce cas, lorsque vous fermerez votre tynyStream, votre flux sortant sera toujours ouvert. N'oubliez pas de copier les données:

mStream.CopyTo(tinyStream);
tinyStream.Close();

Maintenant, vous avez un flux de mémoire outStream avec des données zippées

Bugs et bisous pour U

Bonne chance

0
Олег Рубан

S'il vous plaît se référer au lien ci-dessous, Il est éviter d'utiliser double MemoryStream . https://stackoverflow.com/a/53644256/1979406

0
Haryono