valor requestlengthdiskthreshold maximum maximo maxallowedcontentlength length example c# wcf file-upload httpwebrequest streaming

c# - maximo - requestlengthdiskthreshold maximum value



WCF REST, carga transmitida de archivos y propiedad httpRuntime maxRequestLength (2)

He creado un servicio WCF simple para prototipo de carga de archivos. El servicio:

[ServiceContract] public class Service1 { [OperationContract] [WebInvoke(Method = "POST", UriTemplate = "/Upload")] public void Upload(Stream stream) { using (FileStream targetStream = new FileStream(@"C:/Test/output.txt", FileMode.Create, FileAccess.Write)) { stream.CopyTo(targetStream); } } }

Utiliza webHttpBinding con transferMode establecido en "Streamed" y maxReceivedMessageSize , maxBufferPoolSize y maxBufferSize todo configurado en 2GB. httpRuntime ha establecido maxRequestLength en 10MB.

El cliente emite solicitudes HTTP de la siguiente manera:

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(@"http://.../Service1.svc/Upload"); request.Method = "POST"; request.SendChunked = true; request.AllowWriteStreamBuffering = false; request.ContentType = MediaTypeNames.Application.Octet; using (FileStream inputStream = new FileStream(@"C:/input.txt", FileMode.Open, FileAccess.Read)) { using (Stream outputStream = request.GetRequestStream()) { inputStream.CopyTo(outputStream); } }

Ahora, finalmente, lo que está mal:

Al cargar el archivo de 100 MB de gran tamaño, el servidor devuelve HTTP 400 (solicitud incorrecta). He intentado habilitar el seguimiento de WCF, pero no muestra ningún error. Cuando aumente httpRuntime . maxRequestLength a 1GB, el archivo se carga sin problemas. El MSDN dice que maxRequestLength " especifica el límite para el umbral de búfer del flujo de entrada, en KB ".

Esto me lleva a creer que todo el archivo (todos los 100 MB) se almacena primero en el "buffer de flujo de entrada" y solo entonces está disponible para mi método de Upload en el servidor. Realmente puedo ver que el tamaño del archivo en el servidor no aumenta gradualmente (como es de esperar), en cambio, en el momento en que se crea, ya es de 100 MB.

La pregunta: ¿Cómo puedo hacer que esto funcione para que el "búfer de flujo de entrada" sea razonablemente pequeño (por ejemplo, 1 MB) y cuando se desborda, se llama a mi método de carga? En otras palabras, quiero que la carga sea realmente transmitida sin tener que almacenar el archivo entero en ningún lugar.

EDITAR: Ahora descubrí que httpRuntime contiene otra configuración que es relevante aquí: requestLengthDiskThreshold . Parece que cuando el búfer de entrada supera este umbral, ya no se almacena en la memoria, sino en el sistema de archivos. Así que al menos el archivo grande de 100 MB no se guarda en la memoria (esto es lo que más temía), sin embargo, todavía me gustaría saber si hay alguna forma de evitar este búfer por completo .


Esto puede ser un error en la implementación de transmisión. Encontré un artículo de MSDN que sugiere hacer exactamente lo que está describiendo en http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/fb9efac5-8b57-417e-9f71-35d48d421eb4/ . Desafortunadamente, el empleado de Microsoft que sugirió que la solución encontró un error en la implementación y no dio seguimiento a los detalles de una solución.

Dicho esto, parece que la implementación está rota, lo que se podría probar perfilando su código con un generador de perfiles de memoria y verificando si todo el archivo se está almacenando en la memoria. Si todo el archivo se almacena en la memoria, no podrá solucionar este problema, a menos que alguien encuentre un problema de configuración con su código.

Dicho esto, mientras que el uso de requestLengthDiskThreshold podría funcionar técnicamente, aumentará dramáticamente sus tiempos de escritura ya que cada archivo tendrá que escribirse primero como datos temporales, leer de datos temporales, escribir nuevamente como final y finalmente eliminar los datos temporales. Como ya ha dicho, está tratando con archivos extremadamente grandes, así que dudo que esa solución sea aceptable.

Lo mejor es usar un marco de fragmentación y reconstruir manualmente el archivo. Encontré instrucciones sobre cómo escribir dicha lógica en http://aspilham.blogspot.com/2011/03/file-uploading-in-chunks-using.html pero no he tenido tiempo de verificar su exactitud.

Lo siento, no puedo decirle por qué su código no funciona como se documenta, pero algo similar al segundo ejemplo debería poder funcionar sin aumentar su huella de memoria.


Si está utilizando .NET 4 y aloja su servicio en IIS7 +, puede verse afectado un error de ASP.NET que se describe en la siguiente publicación del blog:

http://blogs.microsoft.co.il/blogs/idof/archive/2012/01/17/what-s-new-in-wcf-4-5-improved-streaming-in-iis-hosting.aspx

Básicamente, para las solicitudes distribuidas, el controlador ASP.NET en IIS almacenará la solicitud completa antes de entregar el control a WCF. Y este controlador obedece el límite de maxRequestLength .

Por lo que sé, no hay una solución para el error y tiene las siguientes opciones:

  • actualizar a .NET 4.5
  • auto alojar su servicio en lugar de utilizar IIS
  • use un enlace que no esté basado en HTTP, de modo que el controlador ASP.NET no esté involucrado