概要
GZipStreamの挙動が変更されており、一定サイズ以上のデータが解凍できなくなった
そこで正しい解凍方法の共有する
変更内容
GZipStream.Readが指定したバイト数を読み込むとは限らなくなった
コード
変更前
before.cs
static byte[] Decode(byte[] src)
{
var length = BitConverter.ToInt32(src, src.Length - 4);
var bytes = new byte[length];
using var ms = new MemoryStream(src);
using var gs = new GZipStream(ms, CompressionMode.Decompress);
gs.Read(bytes, 0, bytes.Length);
return bytes;
}
変更後
after.cs
static byte[] Decode(byte[] src)
{
var length = BitConverter.ToInt32(src, src.Length - 4);
var bytes = new byte[length];
using var ms = new MemoryStream(src);
using var gs = new GZipStream(ms, CompressionMode.Decompress);
int offset = 0;
while (offset < bytes.Length)
{
var readSize = gs.Read(bytes, offset, bytes.Length - offset);
if (readSize == 0) break;
offset += readSize;
}
return bytes;
}
比較
- gs.Read(bytes, 0, bytes.Length);
+ int offset = 0;
+ while (offset < bytes.Length)
+ {
+ var readSize = gs.Read(bytes, offset, bytes.Length - offset);
+ if (readSize == 0) break;
+ offset += readSize;
+ }
余談
gzipファイルは後ろ4バイトにValidate用の解凍後のサイズが入っている
こちらを使うことで、あらかじめ解凍後のサイズのバッファを用意して不要なリサイズを避ける
var length = BitConverter.ToInt32(src, src.Length - 4);
.Net 7からの新機能
Stream.ReadExactlyを使うことで、必ず指定したバイト数分読むことが保証されるようになった
公式ドキュメント