.NET 6 ZlibStream压缩解压后数据大小不一致问题排查
.NET 6 ZlibStream压缩解压后数据大小不一致问题排查
问题描述
使用.NET 6的ZlibStream处理数据压缩时,压缩后立即解压得到的数据与原始数据大小不一致。此前使用MiniZ依赖时数据大小正常,重构移除MiniZ后出现该问题。
原代码实现
压缩解压工具类:
public static class ZLib { public static byte[] CompressData(Stream inStream, CompressionLevel level = CompressionLevel.Optimal) { using (var outStream = new MemoryStream()) { using (var compressStream = new ZLibStream(outStream, level, false) ) { inStream.CopyTo(compressStream); return outStream.ToArray(); } } } public static byte[] DecompressData(Stream inStream) { using (var outStream = new MemoryStream()) { using (var decompressStream = new ZLibStream(inStream, CompressionMode.Decompress)) { decompressStream.CopyTo(outStream); } return outStream.ToArray(); } } public static byte[] CompressData(byte[] inData, CompressionLevel compressionLevel = CompressionLevel.Optimal) { using (var inStream = new MemoryStream(inData, false)) return CompressData(inStream, compressionLevel); } public static byte[] DecompressData(byte[] inData) { using (var inStream = new MemoryStream(inData, false)) return DecompressData(inStream); } }
使用方式:
private byte[] _texture32Data = ...; //...// byte[] texture32Compressed = ZLib.CompressData(_texture32Data, compressionLevel); var texture32UncompressedSize = _texture32Data.Length; var uncompressed = ZLib.DecompressData(texture32Compressed); Debug.Assert(uncompressed.Length == texture32UncompressedSize); // 断言触发
断言失败的数值:
uncompressed.Length = 10153463
texture32UncompressedSize = 10223616
问题原因
核心错误在于压缩流未完成收尾操作就提前读取输出流:
- 在
CompressData方法中,return outStream.ToArray()语句写在ZLibStream的using块内部,此时ZLibStream尚未执行Dispose操作。 ZLibStream需要在Dispose时才会完成压缩的收尾工作(包括写入压缩结束标记、刷新缓冲区中剩余的压缩数据到输出流)。提前读取会导致输出流中的压缩数据不完整,解压后自然得到不完整的原始数据。
修复方案
有两种可行的修复方式:
方式1:将return语句移到ZLibStream的using块外部
利用using块自动调用Dispose的特性,确保压缩流完成所有数据写入后再读取输出流:
public static byte[] CompressData(Stream inStream, CompressionLevel level = CompressionLevel.Optimal) { using (var outStream = new MemoryStream()) { using (var compressStream = new ZLibStream(outStream, level, false)) { inStream.CopyTo(compressStream); } // 此处ZLibStream自动Dispose,完成压缩收尾 return outStream.ToArray(); } }
方式2:手动调用FlushFinalBlock()触发压缩收尾
在数据复制完成后,手动调用FlushFinalBlock()强制将所有剩余压缩数据写入输出流:
public static byte[] CompressData(Stream inStream, CompressionLevel level = CompressionLevel.Optimal) { using (var outStream = new MemoryStream()) { using (var compressStream = new ZLibStream(outStream, level, false)) { inStream.CopyTo(compressStream); compressStream.FlushFinalBlock(); // 手动完成压缩收尾 return outStream.ToArray(); } } }
验证说明
修复后,压缩生成的数据是完整的,解压后的字节数组长度将与原始数据完全一致,断言不再触发。此前MiniZ正常是因为其API实现会自动完成压缩数据的完整写入,无需依赖流的Dispose操作。
内容的提问来源于stack exchange,提问作者user3001150
相关产品推荐
相关产品推荐

