WCF WebService中的GZip压缩

9

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

我有一个连接到这个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 头)。

1
IIS应该有一个可以为任何托管服务添加压缩支持的选项。无法通过自定义编码实现GZip压缩。 - Viacheslav Smityukh
1
好的,我明白了。那么我该如何在WCF Web服务中使用GZip压缩呢?因为我需要传输大量文本数据。 - AndreaCi
我大约2-3年前经历了整个痛苦的过程。一直在尝试找到我当时找到的解决方案,但迄今为止没有运气。同时点赞。 - leppie
2
更新:我找到了二进制文件,并通过Reflector查看了代码。在我的情况下,我只设置了“AutomaticDecompression”,没有其他任何设置。将继续查看是否涉及其他代码。 - leppie
我花了很多时间尝试实现它,但是没有成功。好吧,我们已经在C#服务器和Java客户端中实现了自定义数据压缩。 - Viacheslav Smityukh
3个回答

4
我已经使用DataContract将DataTable对象传输到WCF中。您需要按照以下方式创建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);
            }
        }
    }
}

将以下内容添加到您的项目中,并在您的Service中按如下方式调用以进行压缩:
dt.Data = CompressedBinaryConverter.ToByteArray(data);

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

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

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

0

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


Protobuf对于一直在使用它的人来说是一个很大的痛苦,有很多限制。但无论如何,你都不能将其用于公共合同。 - Viacheslav Smityukh
这个问题表明他们控制着客户端和服务器两方。你能指引我去哪里找到“大痛点”文章或者一些摘要,以便我更好地了解问题吗? - Petar Vučetin
1
简单来说,如果您发送一个空集合,您将收到null而不是相同的空集合。 - Viacheslav Smityukh

0
解决了! 问题中的代码已足够支持服务引用。 如果使用 Web 引用,请同时添加该行。

my_service_object.EnableDecompression = true;

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