2012-04-12 30 views
9

Tengo un servicio web .NET 3.5 alojado en IIS7.5.Compresión GZip en WCF WebService

Tengo una aplicación cliente que se conecta a este servicio web.

que cambiar (en la aplicación cliente) el método httpWebRequest.Create añadir automaticDecompression para GZip pero no está funcionando

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; 
    } 

De este modo, la solicitud se envía correctamente, la respuesta está codificada en gzip (lo veo desde Fiddler), pero se produce una excepción:

Response is not wellformed XML 

(creo que el cliente no decodifica la respuesta)

Si quito esta fila, como en documentación de MSDN

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

La respuesta no está codificado GZip (y en la solicitud no hay Accept-Encoding)

+1

El IIS debe tener la posibilidad de añadir soporte de compresión para cualquier servicio alojado. No hay forma de implementar la compresión GZip a través de una codificación personalizada. –

+1

Yeh ... ok ... ¿y cómo puedo usar la compresión GZip en los servicios web de WCF? Debido a que tengo que transferir una gran cantidad de datos de texto ... – AndreaCi

+0

Pasé por todo este doloroso proceso hace unos 2-3 años. He intentado encontrar la solución que encontré, pero hasta ahora no tuve suerte. +1 mientras tanto. – leppie

Respuesta

0

¡Resuelto! El código en la pregunta fue suficiente para las referencias del servicio. Si está utilizando referencias Web, añada también la línea

my_service_object.EnableDecompression = true; 
0

Una forma posible sería utilizar protobuf para lograr la compresión con el servicio WCF si controlas tanto cliente y servidor.

+0

El protobuf es un gran problema para todos los que lo han usado, hay muchas restricciones aquí. Pero de cualquier forma no puedes usarlo para un contrato público. –

+0

La pregunta indica que tienen el control de ambos lados, cliente y servidor. ¿Puedes señalarme el artículo sobre el "gran dolor" o algún resumen para obtener una mejor idea de cuáles son los problemas? –

+1

El más simple es que si envía una colección vacía, recibirá nulo en su lugar la misma colección vacía. –

4

He hecho esto para transferir objetos DataTable usando WCF con DataContract. Usted tiene que crear el DataContract de la siguiente manera:

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

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

Entonces creó un convertidor binario que convertirá automáticamente cualquier objeto a una matriz de bytes que entonces puedo comprimir mediante 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); 
      } 
     } 
    } 
} 

volcado esto en su proyecto y llamada como ésta en su servicio para comprimir:

dt.Data = CompressedBinaryConverter.ToByteArray(data); 

Entonces llaman así en su lado del cliente para convertir de nuevo a un objeto:

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

sí, es la solución a la que me estoy mudando ... pero hay un problema con esto: los objetos de origen y destino son instancias de clases diferentes (debido a diferentes espacios de nombres para los servicios web) – AndreaCi

+0

Pongo cualquier definición de objeto que se comparte entre el servidor y cliente en una DLL separada y los referencia desde el lado del servidor y del cliente. De esta forma solo tienes que definirlo/mantenerlo en un solo lugar y tus definiciones son universales. – MrWuf

Cuestiones relacionadas