1

我需要压缩一个字符串以减小 Web 服务响应的大小。我在 SharpZipLib 示例中看到了单元测试,但并不是我所需要的示例。

在以下代码中,ZipOutputStream 的构造函数返回异常:“No open entry”

        byte[] buffer = Encoding.UTF8.GetBytes(SomeLargeString);
        Debug.WriteLine(string.Format("Original byes of string: {0}", buffer.Length));

        MemoryStream ms = new MemoryStream();
        using (ZipOutputStream zipStream = new ZipOutputStream(ms))
        {
            zipStream.Write(buffer, 0, buffer.Length);
            Debug.WriteLine(string.Format("Compressed byes: {0}", ms.Length));
        }

        ms.Position = 0;
        MemoryStream outStream = new MemoryStream();

        byte[] compressed = new byte[ms.Length];
        ms.Read(compressed, 0, compressed.Length);

        byte[] gzBuffer = new byte[compressed.Length + 4];
        System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
        System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
        string compressedString = Convert.ToBase64String (gzBuffer);

我在哪里偏离了轨道?我是否让这比它应该的更复杂?

4

5 回答 5

6

对于来自 Silverlight 的 Web 服务通信压缩数据,我使用以下代码段:

private byte[] zipText(string text)
{
    if (text == null)
        return null;

    using(Stream memOutput = new MemoryStream())
    {
        using (GZipOutputStream zipOut = new GZipOutputStream(memOutput))
        {
            using (StreamWriter writer = new StreamWriter(zipOut))
            {
                writer.Write(text);

                writer.Flush();
                zipOut.Finish();

                byte[] bytes = new byte[memOutput.Length];
                memOutput.Seek(0, SeekOrigin.Begin);
                memOutput.Read(bytes, 0, bytes.Length);

                return bytes;
            }
        }
    }
}

private string unzipText(byte[] bytes)
{
    if (bytes == null)
        return null;

    using(Stream memInput = new MemoryStream(bytes))
    using(GZipInputStream zipInput = new GZipInputStream(memInput))
    using(StreamReader reader = new StreamReader(zipInput))
    {
        string text = reader.ReadToEnd();

        return text;
    }
}
  1. 我使用 GZip 而不是 Zip 压缩
  2. 预计文本将从类似的环境中读取/写入,因此我没有进行任何额外的编码/解码。

我的案例是 json 数据的压缩。根据我的观察,在某些情况下,大约 95Kb 的文本数据被压缩为 1.5Kb。所以即使数据会被序列化成base 64,反正都是很好的节省流量。

发布了我的答案,这可能会为某人节省一些时间。

于 2014-12-31T14:56:39.450 回答
3

您确定将其转换为 Base 64 后数据会小得多吗?这将使二进制数据(zip)显着膨胀。您不能使用 HTTP 压缩在传输级别解决问题吗?

这是一篇包含完整源代码的帖子,展示了如何进行往返压缩/解压缩。

http://paultechguy.blogspot.com/2008/09/zip-xml-in-memory-for-web-service.html

于 2012-03-22T21:06:57.330 回答
2

您的代码有一些问题:

  1. 使用流时始终刷新数据。

  2. 要从 MemoryStream 读取数据,只需使用:

    字节[] 数据 = ms.ToArray();

  3. Zip 文件是可能包含多个条目(文件)、注释的容器......您可能需要调用 PutNextEntry() 来添加新条目,然后再开始向其写入数据。

  4. 如果您只需要压缩单个数据流(这是您的情况),您最好的选择是简单地使用 deflate(或 gzip)压缩,这意味着压缩单个数据流(实际上 zip 格式在内部使用 gzip 压缩它的条目...).Net 提供了 2 个非常方便的数据压缩类:GZipStream 和 DeflateStream。一个很好的样本可以在这里找到

于 2012-03-22T21:23:17.980 回答
1

在写入数据之前,您需要调用 PutNextEntry 添加标头。

答案复制自:http: //community.sharpdevelop.net/forums/p/5910/16947.aspx

于 2012-03-22T21:06:56.580 回答
0

我发现的最简化的答案是在解压缩/压缩数据时处理字节,并使用设置大小的缓冲区将数据复制到可以随意使用的 Stream 对象:

    /// <summary>
    /// Unzips (inflates) zipped data.
    /// </summary>
    /// <param name="zippedData">The zipped data.</param>
    /// <returns>The inflated data.</returns>
    public Byte[] GUnzip(Byte[] zippedData)
    {
        using (MemoryStream unzippedData = new MemoryStream())
        {
            using (GZipInputStream zippedDataStream = new GZipInputStream(new MemoryStream(zippedData)))
            {
                CopyStream(zippedDataStream, unzippedData);
            }

            return unzippedData.ToArray();
        }
    }

    /// <summary>
    /// zips data.
    /// </summary>
    /// <param name="unzippedData">The unzipped data.</param>
    /// <returns>The zipped data.</returns>
    public Byte[] GZip(Byte[] unzippedData)
    {
        using (MemoryStream zippedData = new MemoryStream())
        {
            using (GZipOutputStream unzippedDataStream = new GZipOutputStream(new MemoryStream(unzippedData)))
            {
                CopyStream(unzippedDataStream, zippedData);
            }

            return zippedData.ToArray();
        }
    }

    /// <summary>
    /// Accepts an inStream, writes it to a buffer and goes out the outStream
    /// </summary>
    /// <param name="inStream">The input Stream</param>
    /// <param name="outStream">The output Stream</param>
    private static void CopyStream(Stream inStream, Stream outStream)
    {
        int nRead = 0;
        // Using a 2k buffer
        Byte[] theBuffer = new Byte[2048];

        while ((nRead = inStream.Read(theBuffer, 0, theBuffer.Length)) > 0)
        {
            outStream.Write(theBuffer, 0, nRead);
        }
    }
于 2018-03-29T17:46:45.047 回答