このテスト関数に問題があり、メモリ内の文字列を取得し、圧縮し、解凍します。圧縮はうまく機能しますが、解凍を機能させることはできません。
//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のバグですか?
コードで起こることは、ストリームを開いたままにしますが、決して閉じないということです。
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);
これは既知の問題です: 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);
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
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;
}
これにより、圧縮/解凍されたストリームは開いたままになり、作成後もそのまま使用できます。
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();
}
}
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);
}
それでも必要な場合は、ブール引数を使用したGZipStreamコンストラクターを使用して(このようなコンストラクターが2つあります)、そこにtrue値を渡すことができます。
tinyStream = new GZipStream(outStream, CompressionMode.Compress, true);
その場合、tynyStreamを閉じても、出力ストリームは開かれたままになります。データをコピーすることを忘れないでください:
mStream.CopyTo(tinyStream);
tinyStream.Close();
これでメモリストリームができました outStream 圧縮データ
Uのバグとキス
がんばろう
以下のリンクを参照してくださいダブルMemoryStreamの使用は避けてください。 https://stackoverflow.com/a/53644256/1979406