【问题标题】:GZip compression in WCF WebServiceWCF WebService 中的 GZip 压缩
【发布时间】:2012-04-12 14:31:56
【问题描述】:

我有一个托管在 IIS7.5 上的 .NET 3.5 Web 服务。

我有一个连接到这个网络服务的客户端应用程序。

我更改(在客户端应用程序中)httpWebRequest.Create 方法为 GZip 添加自动解压缩,但它不起作用

 WebRequest IWebRequestCreate.Create(Uri uri)
    {
        HttpWebRequest httpWebRequest =
            Activator.CreateInstance(
                typeof(HttpWebRequest), 
                BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
                null, 
                new object[] { uri, null }, 
                null) as HttpWebRequest;

        if (httpWebRequest == null)
            return null;

        httpWebRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
        httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;


        return httpWebRequest;
    }

这样请求发送正确,答案用gzip编码(我从Fiddler看到的),但是出现异常:

Response is not wellformed XML

(我认为客户端没有解码答案)

如果我删除这一行,如 MSDN 文档中所述

httpWebRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate");

答案不是 GZip 编码的(并且在请求中没有 ACCEPT-ENCODING 标头)

【问题讨论】:

  • IIS 应该可以为任何托管服务添加压缩支持。没有办法通过自定义编码实现GZip压缩。
  • 是的.. 好的.. 我如何在 WCF Web 服务中使用 GZip 压缩?因为我要传输大量的文本数据..
  • 大约 2-3 年前,我经历了这整个痛苦的过程。一直试图找到我找到的解决方案,但到目前为止还没有运气。同时 +1。
  • 更新:我找到了二进制文件,并设法通过 Reflector 查看了代码。就我而言,我只设置了AutomaticDecompression,没有别的。将查看是否涉及其他代码。
  • 我已经浪费了很多时间来尝试实现它,但我没有成功。好吧,我们已经在 C# 服务器和 Java 客户端中实现了自定义数据压缩。

标签: c# wcf web-services gzip


【解决方案1】:

我这样做是为了使用带有 DataContract 的 WCF 传输 DataTable 对象。您必须按如下方式创建 DataContract:

[DataContract]
public class WCFDataTableContract
{
    [DataMember]
    public byte[] Schema { get; set; }

    [DataMember]
    public byte[] Data { get; set; }
}

然后我创建了一个二进制转换器,它会自动将任何对象转换为字节数组,然后我可以使用 GZip 对其进行压缩。

public static class CompressedBinaryConverter
{
    /// <summary>
    /// Converts any object into a byte array and then compresses it
    /// </summary>
    /// <param name="o">The object to convert</param>
    /// <returns>A compressed byte array that was the object</returns>
    public static byte[] ToByteArray(object o)
    {
        if (o == null)
            return new byte[0];

        using (MemoryStream outStream = new MemoryStream())
        {
            using (GZipStream zipStream = new GZipStream(outStream, CompressionMode.Compress))
            {
                using (MemoryStream stream = new MemoryStream())
                {
                    new BinaryFormatter().Serialize(stream, o);
                    stream.Position = 0;
                    stream.CopyTo(zipStream);
                    zipStream.Close();
                    return outStream.ToArray();
                }
            }
        }
    }

    /// <summary>
    /// Converts a byte array back into an object and uncompresses it
    /// </summary>
    /// <param name="byteArray">Compressed byte array to convert</param>
    /// <returns>The object that was in the byte array</returns>
    public static object ToObject(byte[] byteArray)
    {
        if (byteArray.Length == 0)
            return null;

        using (MemoryStream decomStream = new MemoryStream(byteArray), ms = new MemoryStream())
        {
            using (GZipStream hgs = new GZipStream(decomStream, CompressionMode.Decompress))
            {
                hgs.CopyTo(ms);
                decomStream.Close();
                hgs.Close();
                ms.Position = 0;
                return new BinaryFormatter().Deserialize(ms);
            }
        }
    }
}

将其转储到您的项目中并在您的服务中像这样调用以进行压缩:

dt.Data = CompressedBinaryConverter.ToByteArray(data);

然后在客户端这样调用它以转换回对象:

dt = (DataTable)CompressedBinaryConverter.ToObject(wdt.Data);

【讨论】:

  • 是的,这是我要迁移到的解决方案。但有一个问题:源对象和目标对象是不同类的实例(因为 web 服务的命名空间不同)
  • 我将在服务器和客户端之间共享的任何对象定义放入一个单独的 DLL 中,并从我的服务器端和客户端引用它们。这样,您只需在一个地方定义/维护它,您的定义是通用的。
【解决方案2】:

如果您同时控制客户端和服务器,一种可能的方法是使用protobuf 通过 WCF 服务实现压缩。

【讨论】:

  • protobuf 对每个一直在使用它的人来说都是一个很大的痛苦,这里有很多限制。但无论如何,您都不能将其用于公共合同。
  • 问题表明他们控制着双方,客户端和服务器。您能否将我指向“大痛苦”文章或一些摘要,我可以更好地了解问题所在?
  • 简单的一个是如果您发送一个空集合,您将收到 null 而不是相同的空集合。
【解决方案3】:

解决了!! 问题中的代码足以用于服务参考。 如果您使用的是 Web 引用,请同时添加行

my_service_object.EnableDecompression = true;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-21
    • 1970-01-01
    • 2016-05-26
    • 2015-07-14
    • 2010-12-17
    • 1970-01-01
    • 2013-03-18
    • 1970-01-01
    相关资源
    最近更新 更多