web-dev-qa-db-ja.com

System.IO.MemoryStreamでGZipStreamを使用するにはどうすればよいですか?

このテスト関数に問題があり、メモリ内の文字列を取得し、圧縮し、解凍します。圧縮はうまく機能しますが、解凍を機能させることはできません。

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

特にソースストリーム(outStream)が読み取られている場合は、bigStream outには少なくともデータが含まれている必要があります。これはMSFTのバグですか?

43

コードで起こることは、ストリームを開いたままにしますが、決して閉じないということです。

  • 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);
95
Timwi

これは既知の問題です: 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);
34
Aliostad

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

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

これにより、圧縮/解凍されたストリームは開いたままになり、作成後もそのまま使用できます。

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

MemoryStreamを使用しようとしている(たとえば、別の関数に渡す)が、「閉じたストリームにアクセスできません」という例外を受け取っている場合その後、使用できる別のGZipStreamコンストラクターがあります。

LeaveOpenパラメーターにtrueを渡すことで、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
Snives

それでも必要な場合は、ブール引数を使用したGZipStreamコンストラクターを使用して(このようなコンストラクターが2つあります)、そこにtrue値を渡すことができます。

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

その場合、tynyStreamを閉じても、出力ストリームは開かれたままになります。データをコピーすることを忘れないでください:

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

これでメモリストリームができました outStream 圧縮データ

Uのバグとキス

がんばろう

0

以下のリンクを参照してくださいダブルMemoryStreamの使用は避けてください。 https://stackoverflow.com/a/53644256/1979406

0
Haryono