Как я могу использовать GZipStream с System.IO.MemoryStream? - PullRequest
42 голосов
/ 16 сентября 2010

У меня проблема с этой тестовой функцией, когда я беру строку в памяти, сжимаю ее и распаковываю. Сжатие работает отлично, но я не могу заставить декомпрессию работать.

//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.

Я считаю, что bigStream out должен, по крайней мере, содержать в себе данные, особенно если мой исходный поток (outStream) читается. это ошибка MSFT или моя?

Ответы [ 8 ]

95 голосов
/ 16 сентября 2010

В вашем коде происходит то, что вы продолжаете открывать потоки, но никогда не закрываете их.

  • В строке 2 вы создаете GZipStream.Этот поток не будет ничего записывать в основной поток, пока не почувствует, что настало подходящее время.Вы можете сказать это, закрыв его.

  • Однако, если вы закроете его, он также закроет основной поток (outStream).Поэтому вы не можете использовать mStream.Position = 0 для него.

Вы всегда должны использовать using, чтобы все ваши потоки были закрыты.Вот вариант вашего кода, который работает.

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);
33 голосов
/ 16 сентября 2010

Это известная проблема: http://blogs.msdn.com/b/bclteam/archive/2006/05/10/592551.aspx

Я немного изменил ваш код, так что этот работает:

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);
5 голосов
/ 25 апреля 2013

Еще одна реализация, в 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
2 голосов
/ 26 августа 2016

Способ сжатия и распаковки в MemoryStream и обратно:

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

Это оставляет сжатый / распакованный поток открытым и, следовательно, пригодным для использования после его создания.

1 голос
/ 03 июля 2016

Если вы пытаетесь использовать MemoryStream (например, передаваете его в другую функцию), но получаете исключение «Не удается получить доступ к закрытому потоку». затем есть другой конструктор GZipStream, который вы можете использовать, который поможет вам.

Передав значение true параметру leftOpen, вы можете указать GZipStream оставить поток открытым после удаления самого себя, по умолчанию он закрывает целевой поток (чего я не ожидал). 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 голос
/ 07 июля 2015
    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();
        }
    }
0 голосов
/ 06 декабря 2018

Пожалуйста, обратитесь к ссылке ниже, Избегайте использования двойной MemoryStream . https://stackoverflow.com/a/53644256/1979406

0 голосов
/ 28 августа 2018

Если вам все еще это нужно, вы можете использовать конструктор GZipStream с логическим аргументом (есть два таких конструктора) и передать туда истинное значение:

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

В этом случае, когда вы закрываете tynyStream,выходной поток все еще будет открыт.Не забудьте скопировать данные:

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

Теперь у вас есть поток памяти outStream с заархивированными данными

Ошибки и поцелуи для U

Удачи

...