GZipStream压缩长字符串时约48KB后出现数据截断问题
GZip压缩长字符串时末尾内容丢失/被替换为空字节的问题修复
问题场景
当使用C#的GZipStream压缩超过约24000个UTF16字符(约48KB)的长字符串时,压缩后的字节流末尾部分会被替换为空字节,解压后内容从第24300个字符开始全为空字符;使用gzip -d命令解压对应文件时,会提示"文件意外结束",确认问题出在压缩环节。
根本原因
问题源于未正确触发GZipStream的缓冲区刷新与结束标志写入。GZipStream在压缩过程中会将数据暂存到内部缓冲区,仅当缓冲区填满、调用FlushFinalBlock()方法,或者流被释放(using块结束)时,才会将剩余的缓冲数据写入目标流,并添加GZip格式要求的结束标识。如果在GZipStream未完成上述操作时就读取目标流内容,会导致末尾的压缩数据丢失,最终表现为解压后内容缺失或被空字节填充。
错误代码示例(导致问题的写法)
public static byte[] CompressString(string input) { byte[] inputBytes = Encoding.Unicode.GetBytes(input); using (var ms = new MemoryStream()) { using (var gzip = new GZipStream(ms, CompressionMode.Compress)) { gzip.Write(inputBytes, 0, inputBytes.Length); return ms.ToArray(); // 提前读取流,GZipStream未关闭,缓冲数据未写入 } } }
修复方案
方案1:利用using块的自动释放机制
通过设置leaveOpen: true确保MemoryStream在GZipStream释放后仍可读取,待GZipStream完成缓冲写入后再获取流内容:
public static byte[] CompressString(string input) { byte[] inputBytes = Encoding.Unicode.GetBytes(input); using (var ms = new MemoryStream()) { // leaveOpen: true 表示GZipStream释放时不关闭MemoryStream using (var gzip = new GZipStream(ms, CompressionMode.Compress, leaveOpen: true)) { gzip.Write(inputBytes, 0, inputBytes.Length); } // GZipStream释放时自动刷新缓冲并写入结束标志 ms.Position = 0; return ms.ToArray(); } }
方案2:手动调用FlushFinalBlock()
显式触发缓冲数据写入与结束标志添加:
public static byte[] CompressString(string input) { byte[] inputBytes = Encoding.Unicode.GetBytes(input); using (var ms = new MemoryStream()) { using (var gzip = new GZipStream(ms, CompressionMode.Compress)) { gzip.Write(inputBytes, 0, inputBytes.Length); gzip.FlushFinalBlock(); // 强制写入剩余缓冲数据和GZip结束标识 } return ms.ToArray(); } }
验证结果
修复后,无论字符串长度如何,压缩和解压后的内容完全一致;使用gzip -d命令解压生成的文件时,不会再出现"文件意外结束"的错误。
内容的提问来源于stack exchange,提问作者DPD-
相关产品推荐
相关产品推荐

