You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何获取解压后GZipStream的Position与Length?

哈哈,这个坑我之前踩过!你遇到的NotSupportedException完全是因为GZipStream的设计特性——它根本不支持Position和Length这俩属性,咱们来一步步解决这个问题。

先搞懂为什么会报错

先明确官方文档里的核心说明:

  • GZipStream.Length 属性完全不被支持,调用时一定会抛出 NotSupportedException(这是重写自Stream基类的行为)
  • GZipStream.Position 属性同样不支持读取或设置,因为它是流式处理的解压流——数据是边读边解压的,没办法提前知道解压后的总长度,也不支持随意跳转位置(毕竟压缩数据是连续流式的,没法直接定位到某个解压后的位置)

所以你想用Position和Length来判断是否到文件末尾,从根上就行不通。

正确判断读取结束的方法

咱们换个思路,通过读取操作的返回值来判断流是否结束,这也是流式处理的标准做法:

方法1:用BinaryReader的基础读取方法判断

如果是逐个字节读取,用ReadByte()——当它返回-1时,就说明已经读到流的末尾了:

using (var fileStream = new FileStream("target.gz", FileMode.Open))
using (var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress))
using (var binaryReader = new BinaryReader(gzipStream))
{
    int currentByte;
    // 循环读取直到返回-1
    while ((currentByte = binaryReader.ReadByte()) != -1)
    {
        // 处理读取到的字节
        Console.WriteLine($"读取到字节:{currentByte}");
    }
}

如果是按块读取(效率更高),用Read(byte[] buffer, int offset, int count)——当它返回0时,说明没有更多数据可读了:

byte[] buffer = new byte[4096]; // 4KB的缓冲区,可根据需求调整
int bytesRead;

using (var fileStream = new FileStream("target.gz", FileMode.Open))
using (var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress))
using (var binaryReader = new BinaryReader(gzipStream))
{
    while ((bytesRead = binaryReader.Read(buffer, 0, buffer.Length)) > 0)
    {
        // 处理本次读取到的bytesRead个字节
        // 比如写入另一个文件,或者解析数据
        Console.WriteLine($"本次读取了{bytesRead}字节");
    }
}

方法2:如果必须要知道解压后的总长度(可选)

如果你业务上一定要提前知道解压后的数据大小,有个小技巧:标准GZip文件的末尾会用4字节存储原始未压缩数据的长度(注意:这个长度是32位无符号整数,所以只适用于原始文件≤4GB的情况)。你可以先读取这个值,再解压:

long uncompressedSize = 0;

using (var fileStream = new FileStream("target.gz", FileMode.Open))
{
    // 跳到文件末尾的前4字节位置
    fileStream.Seek(-4, SeekOrigin.End);
    byte[] sizeBuffer = new byte[4];
    fileStream.Read(sizeBuffer, 0, 4);
    // 把字节数组转成无符号整数
    uncompressedSize = BitConverter.ToUInt32(sizeBuffer, 0);
    
    // 回到文件开头准备解压
    fileStream.Seek(0, SeekOrigin.Begin);

    using (var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress))
    using (var binaryReader = new BinaryReader(gzipStream))
    {
        // 直接读取全部解压后的数据
        byte[] fullData = new byte[uncompressedSize];
        binaryReader.Read(fullData, 0, fullData.Length);
        
        // 处理完整数据
        Console.WriteLine($"解压后总大小:{uncompressedSize}字节");
    }
}

⚠️ 注意:如果原始文件超过4GB,这个方法就会失效,因为32位整数存不下这么大的数值。这种情况下还是老老实实用流式读取的方法更稳妥。

最后总结一下

  • 彻底放弃用GZipStream的Position和Length属性判断末尾,这俩属性本来就不是给它设计的
  • 用BinaryReader.ReadByte() == -1或者BinaryReader.Read(...) == 0来判断流是否结束,这是流式处理的标准姿势
  • 非要知道解压后长度的话,用GZip文件末尾的4字节字段,但要注意4GB的限制

内容的提问来源于stack exchange,提问作者Russell Butler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:15:28