c# wcf iis

c# - Dificultad para usar WCF para subir archivos grandes



iis (3)

¿Es posible comprimir los archivos ?, sé que no es una solución pero puede ayudar si conoce el tamaño máximo que va a ser compatible.

Si no, la única forma de evitarlo es transmitir el archivo o dividirlo en varias partes.

Hay otras preguntas similares sobre SO acerca de esto. Desafortunadamente, muchos parecen ser incautos entre sí en algún aspecto. Espero que este ayude a otros y ponga en reposo otras preguntas.

El requisito de mi proyecto es cargar archivos de 250 MB a través de IIS en un servicio WCF backend alojado en IIS. Creé algunas pruebas unitarias para el servicio WCF backend alojado en IIS. Son:

1) Upload 1MB File 2) Upload 5MB File 3) Upload 10MB file 4) Upload 20MB File 5) Upload 200MB File

Desde el principio, es probable que tengamos claro que necesitamos estar usando algún tipo de transferencia de archivos en secuencias o fragmentadas. Utilicé esta muestra .

El ejemplo describe un método que utiliza el objeto Stream de .NET. Un efecto secundario de usar el objeto de flujo es que debe usar los contratos de mensajes. No es suficiente poner la secuencia en la lista de parámetros de su función. Así que hacemos eso.

Por defecto, el web.config para este servicio WCF es bastante magro. Y nada funciona:

System.ServiceModel.ProtocolException: The remote server returned an unexpected response: (400) Bad Request. ---> System.Net.WebException: The remote server returned an error: (400) Bad Request.

Después de mucho buscar y experimentar, está claro que BasicHttpBinding es incompatible con esta combinación del objeto Stream y el MessageContract. Debemos cambiar a WSHttpBinding .

Para hacer esto, web.config del servidor se vuelve un poco más complejo en la sección:

<system.serviceModel> <behaviors> <serviceBehaviors> <behavior> <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --> <serviceMetadata httpGetEnabled="true"/> <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> <serviceDebug includeExceptionDetailInFaults="true" httpHelpPageEnabled="true"/> </behavior> <behavior name="FileServiceBehavior"> <serviceMetadata httpGetEnabled="true"/> <dataContractSerializer maxItemsInObjectGraph="2147483647"/> <serviceDebug includeExceptionDetailInFaults="true"/> <serviceThrottling maxConcurrentCalls="500" maxConcurrentSessions="500" maxConcurrentInstances="500"/> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> <bindings> <wsHttpBinding> <binding name="FileServiceBinding" closeTimeout="10:01:00" maxBufferPoolSize="104857600" maxReceivedMessageSize="104857600" openTimeout="10:01:00" receiveTimeout="10:10:00" sendTimeout="10:01:00" messageEncoding="Mtom"> <readerQuotas maxDepth="104857600" maxStringContentLength="104857600" maxArrayLength="104857600" maxBytesPerRead="104857600" maxNameTableCharCount="104857600" /> </binding> </wsHttpBinding> </bindings> <services> <service behaviorConfiguration="FileServiceBehavior" name="OMS.Service.FileService"> <endpoint address="" binding="wsHttpBinding" bindingConfiguration="FileServiceBinding" contract="OMS.Service.IFileService"></endpoint> </service> </services> </system.serviceModel>

Sin más trabajo del que hablar, el archivo de 1 MB ahora pasa sin ningún problema.

Para poder pasar archivos de más de 4 MB, debe ajustar una configuración en web.config en IIS (lado del servidor de su servicio WCF). Este artículo de Microsoft explica qué es esa configuración. Por ejemplo, si lo configura en 8192, entonces podrá cargar el archivo de 5MB, pero no algo más grande.

<httpRuntime maxRequestLength="8192" />

Puse el mío en algo obsceno para las pruebas: 2147483647. Los primeros 4 archivos pasan esta puerta.

Los 200MB no tuvieron la oportunidad de llegar a esta puerta por la siguiente razón:

System.InsufficientMemoryException: Failed to allocate a managed memory buffer of 279620368 bytes. The amount of available memory may be low. ---> System.OutOfMemoryException: Exception of type ''System.OutOfMemoryException'' was thrown.

La explicación de este problema se describe muy bien en este póster .

Piensa en esto, de esta manera. El archivo de 200MB nunca salió del cliente. El cliente debe cargarlo completamente, cifrarlo y luego transmitirlo al servidor.

Cuando utiliza Visual Studio 2010 para generar las clases de proxy para el servicio, coloca algunas cosas en su app.config. Para mí, se ve así:

<binding name="Binding_IFileService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Mtom" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /> <security mode="Message"> <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" /> <message clientCredentialType="Windows" negotiateServiceCredential="true" /> </security> </binding>

La clave es el modo de seguridad. Se establece en "mensaje" de forma predeterminada. Ese valor es recogido por lo que se establece en el servidor. Por defecto, su servidor usa seguridad de nivel de mensaje.

Si intentas forzarlo en el servidor para que sea así:

<security mode="None">

Usted recibe este error:

System.ServiceModel.EndpointNotFoundException: There was no endpoint listening at http://localhost:8080/oms/FileService.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. ---> System.Net.WebException: The remote server returned an error: (404) Not Found.

(Recordé actualizar el proxy del cliente)

Y así, eso es lo que significa para mí ... ¡Ayuda!


Buena pregunta detallada :)

Su último mensaje de error fue el archivo 404 no encontrado. Por lo general, esto es que falta el archivo o que el sitio está inactivo. Marque esto solo para descartar esto.

  • Prueba y busca tu archivo svc
  • Prueba de que todavía funciona con un archivo más pequeño.

Según el último cambio, ha desactivado el cifrado basado en mensajes. Este cambio debe realizarse tanto en el cliente como en el servidor. Si un lado está encriptando y el otro no espera que el mensaje sea encriptado, se confunde.


Por lo general, WCF no maneja bien las transferencias de archivos grandes a menos que implemente la transmisión, lo que en realidad se puede lograr con BasicHttpBindings.

Para mi proyecto, tengo una fábrica de host personalizada que crea hosts de servicio:

protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) { ServiceHost host = new ServiceHost(serviceType, baseAddresses); ContractDescription contract = ContractDescription.GetContract(serviceType); BasicHttpBinding binding = new BasicHttpBinding(); binding.OpenTimeout = TimeSpan.FromMinutes(1); binding.ReceiveTimeout = TimeSpan.FromMinutes(1); binding.SendTimeout = TimeSpan.FromHours(1); binding.TransferMode = TransferMode.StreamedResponse; binding.MessageEncoding = WSMessageEncoding.Mtom; ServiceEndpoint streaming = new ServiceEndpoint(contract, binding, new EndpointAddress(baseAddresses[0] + "/STREAMING")); host.AddServiceEndpoint(streaming); return host; }

Usted querrá usar StreamedRequest en su caso.

Y la implementación del StreamingService:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single, AddressFilterMode = AddressFilterMode.Any)] public class FileStreamingService : IFileStreamingV1 { Stream IFileStreamingV1.GetFileStream(string downloadFileLocation) { if (!File.Exists(downloadFileLocation)) { throw new FaultException("The file could not be found"); } FileStream stream = File.OpenRead(downloadFileLocation); return stream; } }

No especifiqué un tamaño máximo de búfer en el servicio en sí mismo, pero la aplicación cliente en mi caso aceleró la descarga a algo así como 5mb por segundo. En su caso, el servicio deberá configurar el comportamiento del acelerador. Esto no resuelve el problema de cómo le dirá al servicio cuántos bytes hay en el archivo para transmitirlo correctamente, pero debería darle un comienzo.

También debe tener en cuenta el uso de MTOM en la configuración del host. MTOM fue diseñado para ayudar con la transferencia de archivos grandes (no tan bueno para transferencias pequeñas).

No tengo un ejemplo del comportamiento del cliente, pero su servicio debería leer el número de bytes del búfer de la secuencia de carga y guardarlos en el archivo hasta que no quede nada para transmitir. Aunque la memoria es barata, no recomendaría almacenar una copia completa en la memoria del archivo, especialmente a 200 mb.

También debe tener en cuenta que, dependiendo de su plataforma de alojamiento web (IIS, Apache, etc.), también puede estar limitado a la cantidad de datos que se pueden transferir en un momento dado. Sin embargo, un cambio en la configuración generalmente puede resolver cualquier problema de alojamiento.

Espero que esto ayude.