GZipStream解压数据异常求助:压缩后解压长度不一致
问题出在GZipStream的压缩收尾步骤上!
嘿,我一眼就看穿问题所在了——你没有正确完成GZipStream的压缩流程,导致压缩后的数据流不完整,解压自然得不到和原数据一样的长度。
为什么会这样?
GZipStream在压缩时会把数据暂存到内部缓冲区,只有缓冲区满或者明确触发收尾操作时,才会把所有压缩数据(包括GZip格式必须的结束标记)写入目标流。你现在只调用了Flush(),它只会把当前缓冲区的内容刷到ms2,但不会写入结束标记——而这个标记是解压时识别压缩数据完整性的关键,缺少它的话,解压过程会提前终止,自然就得不到完整的原始数据了。
修正后的代码
用using语句包裹GZipStream是最稳妥的方式,它会在代码块结束时自动调用Dispose(),而Dispose()内部会帮你完成FlushFinalBlock()(收尾压缩)的操作,同时还能保证即使出现异常也会正确释放资源:
// Serialize var ms = new MemoryStream(); ProtoBuf.Serializer.Serialize(ms, result); ms.Seek(0, SeekOrigin.Begin); // Compress - 使用using自动处置流,完成压缩收尾 var ms2 = new MemoryStream(); using (var zipStream = new GZipStream(ms2, CompressionMode.Compress, leaveOpen: true)) { ms.CopyTo(zipStream); } // 此处自动调用Dispose,写入GZip结束标记 ms2.Seek(0, SeekOrigin.Begin); // Test var ms3 = new MemoryStream(); using (var unzipStream = new GZipStream(ms2, CompressionMode.Decompress)) { unzipStream.CopyTo(ms3); } System.Diagnostics.Debug.WriteLine($"{ms.Length} =? {ms3.Length}");
关键细节说明
leaveOpen: true参数很重要:它告诉GZipStream在Dispose时不要关闭ms2,这样我们后续还能继续操作这个内存流。- 如果你不想用
using,也可以手动调用zipStream.FlushFinalBlock()替代Flush(),但一定要在ms.CopyTo(zipStream)之后调用,然后再关闭zipStream。不过using是更推荐的C#最佳实践,能避免遗漏资源释放。
现在再运行代码,你应该就能看到压缩前后的长度完全一致啦!
内容的提问来源于stack exchange,提问作者Spook
相关产品推荐
相关产品推荐

