.NET GZipStream 压缩和解压缩

15

以下代码有什么问题?执行后我始终得到 FALSE,也就是说压缩解压后的数据与原始值不匹配。

public static bool Test()
        {
            string sample = "This is a compression test of microsoft .net gzip compression method and decompression methods";
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            byte[] data = encoding.GetBytes(sample);
            bool result = false;

            //Compress
            MemoryStream cmpStream;
            cmpStream = new MemoryStream();
            GZipStream hgs = new GZipStream(cmpStream, CompressionMode.Compress);
            hgs.Write(data, 0, data.Length);
            byte[] cmpData = cmpStream.ToArray();

            MemoryStream decomStream;
            decomStream = new MemoryStream(cmpData);
            hgs = new GZipStream(decomStream, CompressionMode.Decompress);
            hgs.Read(data, 0, data.Length);

            string sampleOut = System.BitConverter.ToString(data);

            result = String.Equals(sample, sampleOut) ;
            return result;
        }

如果您能指出我的错误,我将非常感激。

4个回答

20

Write调用之后,关闭GZipStream

如果不调用Close,则有可能会存在一些数据被缓存且尚未写入底层流的情况。


7
我刚用反编译工具检查了一下,只有“Close”是一个选项,“Flush”没有实际作用。 - Mehrdad Afshari
是的,我在压缩和解压缩之后都尝试了close,但仍然不起作用。由于某种原因,hgs.Read(...)没有读取任何内容::这就是问题所在。无论是否使用hgs.close(),问题仍然存在。如果您确信close()或flush()可以,请粘贴您的工作代码?非常感谢。=-- Mehdi Anis --= - MehdiAnis
3
正确的做法是 - 不要冲洗 - 把它关上。我排除问题花了一个小时终于弄清楚了。 - Brian Webster
我花了很长时间才想到的。 - Rahul Misra

15

试试这段代码:

public static bool Test()
        {
            string sample = "This is a compression test of microsoft .net gzip compression method and decompression methods";

            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();

            byte[] data = encoding.GetBytes(sample);
            bool result = false;

            // Compress
            MemoryStream cmpStream = new MemoryStream();

            GZipStream hgs = new GZipStream(cmpStream, CompressionMode.Compress);

            hgs.Write(data, 0, data.Length);

            byte[] cmpData = cmpStream.ToArray();

            MemoryStream decomStream = new MemoryStream(cmpData);

            hgs = new GZipStream(decomStream, CompressionMode.Decompress);
            hgs.Read(data, 0, data.Length);

            string sampleOut = encoding.GetString(data);

            result = String.Equals(sample, sampleOut);
            return result;
        }

问题在于你没有使用ASCIIEncoder将sampleData的字符串还原回来。

编辑:这是一份经过清理的代码版本,有助于关闭/处理:

public static bool Test()
        {
            string sample = "This is a compression test of microsoft .net gzip compression method and decompression methods";

            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();

            byte[] data = encoding.GetBytes(sample);

            // Compress.
            GZipStream hgs;
            byte[] cmpData;

            using(MemoryStream cmpStream = new MemoryStream())
            using(hgs = new GZipStream(cmpStream, CompressionMode.Compress))
            {
                hgs.Write(data, 0, data.Length);
                hgs.Close()

                // Do this AFTER the stream is closed which sounds counter intuitive 
                // but if you do it before the stream will not be flushed
                // (even if you call flush which has a null implementation).
                cmpData = cmpStream.ToArray();
            }  

            using(MemoryStream decomStream = new MemoryStream(cmpData))
            using(hgs = new GZipStream(decomStream, CompressionMode.Decompress))
            {
                hgs.Read(data, 0, data.Length);
            }

            string sampleOut = encoding.GetString(data);

            bool result = String.Equals(sample, sampleOut);
            return result;
        }

是的! 它有效了!但实际问题仍然存在。它只能在数据值未改变时有效。如果我在调用hgs.read(... .. .)之前使用" data = new byte[data.Length];"重置data[],结果为false。因为hgs.Read完全失败。它没有读取任何东西。如果您放置“readCount=hgs.Read(...)",则会看到readCount=0,表示未读取任何内容。这就是我面临的问题。希望您能提供一些指导。谢谢。感谢所有人快速响应。 - MehdiAnis
2
抱歉如果我误解了,但您是在说如果在'hgs.Read()'调用之前放置'data = new byte[data.Length];',那么结果就是错误的吗?这正是我所期望的,因为此时data[]数组的值被清除了。不确定我是否理解正确,我必须再喝一些咖啡! :) - Jason Evans
2
我知道这是一个老问题,但@MehdiAnis是正确的。这段代码不起作用。观察hgs.Read(data,0,data.Length)的返回值,你会发现它是零。 - Fantius
在将压缩字节复制到数组之前,您必须先使用Close()方法关闭压缩GZipStream。 - Fantius
@JasonEvans 我改变了ToArray()调用的时间,否则根据其他评论,它就无法工作。要么这样,要么就是一个踩票,我看不出有什么理由踩一个本来完全正确的答案。 - rism

10

解决这个问题需要处理三个问题:

  1. 在使用WRITE GZipStream之后,需要关闭它:hgs.Close();

  2. GZipStream读取时需要使用WHILE循环,并将较小的未压缩数据缓冲区写入MemoryStream:outStream.Write(...);

  3. 转换解压缩的byte[]数组需要使用编码转换:string sampleOut = encoding.GetString(data);

下面是最终的代码:

public static bool Test()
        {
            string sample = "This is a compression test of microsoft .net gzip compression method and decompression methods";
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            byte[] data = encoding.GetBytes(sample);
            bool result = false;

            // Compress 
            MemoryStream cmpStream = new MemoryStream();
            GZipStream hgs = new GZipStream(cmpStream, CompressionMode.Compress, true);

            hgs.Write(data, 0, data.Length);
            hgs.Close();


            //DeCompress
            byte[] cmpData = cmpStream.ToArray();
            MemoryStream decomStream = new MemoryStream(cmpData);

            data = new byte[data.Length];
            hgs = new GZipStream(decomStream, CompressionMode.Decompress, true);

            byte[] step = new byte[16]; //Instead of 16 can put any 2^x
            MemoryStream outStream = new MemoryStream();
            int readCount;

            do
            {
                readCount = hgs.Read(step, 0, step.Length);
                outStream.Write(step, 0, readCount);
            } while (readCount > 0);
            hgs.Close();

            string sampleOut = encoding.GetString(outStream.ToArray());
            result = String.Equals(sample, sampleOut);
            return result; 
        }

我曾经非常困惑如何使用 Microsoft .NET GZipStream 对象进行压缩/解压操作。最终,我认为我找到了正确的方法。非常感谢大家,因为解决方案来自于你们所有人。


4
以下是我改进后的最终解决方案:

  [Test]
  public void Test_zipping_with_memorystream()
  {
   const string sample = "This is a compression test of microsoft .net gzip compression method and decompression methods";
   var encoding = new ASCIIEncoding();
   var data = encoding.GetBytes(sample);
   string sampleOut;
   byte[] cmpData;

   // Compress 
   using (var cmpStream = new MemoryStream())
   {
    using (var hgs = new GZipStream(cmpStream, CompressionMode.Compress))
    {
     hgs.Write(data, 0, data.Length);
    }
    cmpData = cmpStream.ToArray();
   }

   using (var decomStream = new MemoryStream(cmpData))
   {
    using (var hgs = new GZipStream(decomStream, CompressionMode.Decompress))
    {
     using (var reader = new StreamReader(hgs))
     {
      sampleOut = reader.ReadToEnd();
     }
    }
   }

   Assert.IsNotNullOrEmpty(sampleOut);
   Assert.AreEqual(sample, sampleOut);
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接