【问题标题】:DeflateStream not decompressing data (the first time)DeflateStream 不解压数据(第一次)
【发布时间】:2010-11-11 20:27:41
【问题描述】:

所以这里有一个奇怪的。我有这个方法来获取 Base64 编码的放气字符串并返回原始数据:

public static string Base64Decompress(string base64data)
{
    byte[] b = Convert.FromBase64String(base64data);
    using (var orig = new MemoryStream(b))
    {
        using (var inflate = new MemoryStream())
        {
            using (var ds = new DeflateStream(orig, CompressionMode.Decompress))
            {
                ds.CopyTo(inflate);
                return Encoding.ASCII.GetString(inflate.ToArray());
            }
        }
    }
}

这会返回一个空字符串除非我向ds.CopyTo(inflate) 添加第二个调用。 (WTF?)

   ...
            using (var ds = new DeflateStream(orig, CompressionMode.Decompress))
            {
                ds.CopyTo(inflate);
                ds.CopyTo(inflate);
                return Encoding.ASCII.GetString(inflate.ToArray());
            }
   ...

Flush/Close/Disposeds 无效。)

为什么DeflateStream 在第一次调用时会复制 0 个字节?我也尝试使用Read() 循环,但它在第一次调用时也返回零,然后在第二次调用。


更新:这是我用来压缩数据的方法。
public static string Base64Compress(string data, Encoding enc)
{
    using (var ms = new MemoryStream())
    {
        using (var ds = new DeflateStream(ms, CompressionMode.Compress))
        {
            byte[] b = enc.GetBytes(data);
            ds.Write(b, 0, b.Length);
            ds.Flush();
            return Convert.ToBase64String(ms.ToArray());
        }
    }
}

【问题讨论】:

  • 这很有趣。当您将两个ds.CopyTo() 中的第一个替换为ds.Read(...) 时会发生什么?第一个CopyTo() 触发读取流的页脚。 Read() 也应该这样做。只是想知道。
  • 你确定它是 deflate 压缩的,而不是 gzip 压缩的吗?您确定 deflate(或 gzip?)数据前面没有其他内容吗?
  • @Pieter: .Read() 具有相同的效果——它返回0,但会导致对CopyTo() 的下一次调用起作用。
  • @nos:是的。我用 DeflateStream 生成了数据。我还使用了一个外部工具来测试我的 Compress 方法生成的数据,它没有任何抱怨。我也会发布压缩方法。
  • 如果压缩流的最后一个块没有完全写出(即不完整),我之前已经看到过这种情况;第一次调用读取/复制将失败,后续调用将访问数据。我看看能不能翻出一些参考资料……

标签: c# .net-4.0 deflatestream


【解决方案1】:

当压缩字节不完整时会发生这种情况(即,并非所有块都被写出)。

如果我将 Base64Compress 与以下 Decompress 方法一起使用,我将收到 InvalidDataException 消息“未知块类型。流可能已损坏。'

解压

public static string Decompress(Byte[] bytes)
{
  using (var uncompressed = new MemoryStream())
  using (var compressed = new MemoryStream(bytes))
  using (var ds = new DeflateStream(compressed, CompressionMode.Decompress))
  {
    ds.CopyTo(uncompressed);
    return Encoding.ASCII.GetString(uncompressed.ToArray());
  }
}

请注意,使用以下 Compress 方法时一切正常

public Byte[] Compress(Byte[] bytes)
{
  using (var memoryStream = new MemoryStream())
  {
    using (var deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress))
      deflateStream.Write(bytes, 0, bytes.Length);

    return memoryStream.ToArray();
  }
}

更新

糟糕,愚蠢的我......在你处理 DeflateStream 之前你不能 ToArray 内存流(因为实际上没有实现刷新(并且 Deflate/GZip 压缩数据块);最后一个块仅在关闭/处理时写入。

重写压缩为:

public static string Base64Compress(string data, Encoding enc)
{
  using (var ms = new MemoryStream())
  {
    using (var ds = new DeflateStream(ms, CompressionMode.Compress))
    {
      byte[] b = enc.GetBytes(data);
      ds.Write(b, 0, b.Length);
    }

    return Convert.ToBase64String(ms.ToArray());
  }
} 

【讨论】:

  • 是的,这就是问题所在。从技术上讲,您应该使用采用 leaveOpen 参数并传递 true 的 DeflateStream() 重载。没有它,关闭/处置 DeflateStream 也将处置 MemoryStream。现在这不会造成问题是个意外。
  • @Hans,绝对不是一个坏主意,虽然 Disposing a MemoryStream 实际上并没有清除缓冲区;相反,它只会阻止在 MemoryStream 上发生任何进一步的读/写。所以从技术上讲,MemoryStream 上有一个重复的 dispose,无论如何,通过 ToArray 访问的字节仍然可以访问。
  • 是的。我在 SO 有大量的 cr*p 指出处理 MemoryStream 是愚蠢的。很高兴回馈一些:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-29
相关资源
最近更新 更多