【问题标题】:Can't inflate with C# using DeflateStream无法使用 DeflateStream 使用 C# 进行充气
【发布时间】:2013-12-31 02:12:40
【问题描述】:

我正在使用单声道来膨胀/缩小字节。代码如下:

public static byte[] Inflate(byte[] data)
    {
        using (MemoryStream inStream = new MemoryStream(data))
        {
            using (MemoryStream outStream = new MemoryStream())
            {
                using (DeflateStream decompressStream = new DeflateStream(inStream, CompressionMode.Decompress))
                {
                    decompressStream.CopyTo(outStream);
                }
                return outStream.ToArray();
            }
        }
    }

输入数据为: 47bytes。算法是 DEFLATE。

我已经在其他平台上成功地对相同的数据进行了膨胀,但是通过上面的代码,它抛出了以下异常:

System.IO.IOException: Corrupted data ReadInternal
  at System.IO.Compression.DeflateStreamNative.CheckResult (Int32 result, System.String where) [0x00000] in <filename unknown>:0 
  at System.IO.Compression.DeflateStreamNative.ReadZStream (IntPtr buffer, Int32 length) [0x00000] in <filename unknown>:0 
  at System.IO.Compression.DeflateStream.ReadInternal (System.Byte[] array, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.Compression.DeflateStream.Read (System.Byte[] dest, Int32 dest_offset, Int32 count) [0x00000] in <filename unknown>:0 
  at System.IO.Stream.CopyTo (System.IO.Stream destination, Int32 bufferSize) [0x00000] in <filename unknown>:0 
  at System.IO.Stream.CopyTo (System.IO.Stream destination) [0x00000] in <filename unknown>:0 

【问题讨论】:

  • 在 .NET 4.5 上出现类似错误(InvalidDataException,“块长度与其补码不匹配。”)。所以我猜数据真的无效...
  • 我在尝试在 Unity3d 中构建我的世界地图编辑器时遇到此错误。我认为这是因为压缩数据嵌入在另一个二进制文件中,但不是。我将二进制压缩块导出到一个文件中,我可以使用 nodejs zlib 库对其进行扩充。

标签: c# mono deflatestream


【解决方案1】:

如果您阅读我的评论,您会发现我在 18 小时前遇到了这个问题,虽然问题的答案在您的回答中,但它并不直接明显。在您的答案中有变量集wantRfc1950Header = true,在您的输入流中,前两个字节是 RFC 1950 魔术字节78 9cSystem.IO.Compression.DeflateStream 需要一个原始 RFC 1951 流,其中省略了这两个字节。我想你应该能够使用你最初的例子,如果你在将前两个字节喂给充气机之前砍掉它。

不利的一面是,我花了 18 多个小时才发现我需要删除两个字节的数据。从好的方面来说,我更熟悉 zlib 和 Huffman 编码的内部结构。

【讨论】:

    【解决方案2】:

    最后我用DotNetZip:http://dotnetzip.codeplex.com解决了这个问题。

    public static byte[] Inflate(byte[] data)
        {
            int outputSize = 1024;
            byte[] output = new Byte[ outputSize ];
            bool expectRfc1950Header = true;
            using ( MemoryStream ms = new MemoryStream())
            {
                ZlibCodec compressor = new ZlibCodec();
                compressor.InitializeInflate(expectRfc1950Header);
    
                compressor.InputBuffer = data;
                compressor.AvailableBytesIn = data.Length;
                compressor.NextIn = 0;
                compressor.OutputBuffer = output;
    
                foreach (var f in new FlushType[] { FlushType.None, FlushType.Finish } )
                {
                    int bytesToWrite = 0;
                    do
                    {
                        compressor.AvailableBytesOut = outputSize;
                        compressor.NextOut = 0;
                        compressor.Inflate(f);
    
                        bytesToWrite = outputSize - compressor.AvailableBytesOut ;
                        if (bytesToWrite > 0)
                            ms.Write(output, 0, bytesToWrite);
                    }
                    while (( f == FlushType.None && (compressor.AvailableBytesIn != 0 || compressor.AvailableBytesOut == 0)) ||
                        ( f == FlushType.Finish && bytesToWrite != 0));
                }
    
                compressor.EndInflate();
    
                return ms.ToArray();
            }
        }
    
        public static byte[] Deflate(byte[] data)
        {
            int outputSize = 1024;
            byte[] output = new Byte[ outputSize ];
            int lengthToCompress = data.Length;
    
            // If you want a ZLIB stream, set this to true.  If you want
            // a bare DEFLATE stream, set this to false.
            bool wantRfc1950Header = true;
    
            using ( MemoryStream ms = new MemoryStream())
            {
                ZlibCodec compressor = new ZlibCodec();
                compressor.InitializeDeflate(CompressionLevel.BestCompression, wantRfc1950Header);  
    
                compressor.InputBuffer = data;
                compressor.AvailableBytesIn = lengthToCompress;
                compressor.NextIn = 0;
                compressor.OutputBuffer = output;
    
                foreach (var f in new FlushType[] { FlushType.None, FlushType.Finish } )
                {
                    int bytesToWrite = 0;
                    do
                    {
                        compressor.AvailableBytesOut = outputSize;
                        compressor.NextOut = 0;
                        compressor.Deflate(f);
    
                        bytesToWrite = outputSize - compressor.AvailableBytesOut ;
                        if (bytesToWrite > 0)
                            ms.Write(output, 0, bytesToWrite);
                    }
                    while (( f == FlushType.None && (compressor.AvailableBytesIn != 0 || compressor.AvailableBytesOut == 0)) ||
                        ( f == FlushType.Finish && bytesToWrite != 0));
                }
    
                compressor.EndDeflate();
    
                ms.Flush();
                return ms.ToArray();
            }
        }
    

    【讨论】:

      【解决方案3】:

      是的,你只能 +2 个字节,但我不确定这是否允许损坏的数据通过或在所有情况下都有效?

         // Note: Caller must Dispose/Close.
          public DataReader ReadCompressedData()
          {
              // TODO: Optimize when using MemoryStream to use GetBuffer?
              var uncompressedSize = ReadInt32();
              var compressedSize = ReadInt32();
      
              // Consuming 2 bytes for the 78 9C (Sometimes other like 78 DA)
              // Unity / .Net Deflate Stream expects the data to not have this header.
              // I could use the SharpZlib project to get around this or the DotNetZip.
              // https://stackoverflow.com/questions/762614/how-do-you-use-a-deflatestream-on-part-of-a-file
              // http://www.faqs.org/rfcs/rfc1950.html
              // https://stackoverflow.com/questions/20850703/cant-inflate-with-c-sharp-using-deflatestream
              //stream.Position += 2;
              byte[] magic = ReadBytes(2);
              compressedSize -= 2;
              // I wonder if I should check these?
      
              var compressedData = ReadBytes(compressedSize);
              if (compressedData.Length != compressedSize)
              {
                  Debug.LogError("Data read from underlying stream does not match specified size.");
              }
      
              // Decompress the data in the stream leaving it open.
              // Note: Not sure how to stop DeflateStream gobbling up all data in the stream.
              // using (var ds = new DeflateStream(BaseStream, CompressionMode.Decompress, true))
              // {
              //
              // }
      
              // Note: We are trusting that the decompressed data will fit completely into uncompressedSize.
              var os = new MemoryStream(uncompressedSize);
              using (var inputStream = new MemoryStream(compressedData))
              {
                  using (var ds = new DeflateStream(inputStream, CompressionMode.Decompress))
                  {
                      ds.CopyTo(os);
                  }
              }
      
              // Reset the stream to the beginning for reading.
              os.Position = 0;
              return new DataReader(os);
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多