2009-03-03 8 views
34

¿Cuál es la mejor forma de transmitir archivos utilizando ASP.NET?La mejor manera de transmitir archivos en ASP.NET

Parece haber varios métodos para esto, y actualmente estoy usando el método Response.TransmitFile() dentro de un controlador http, que envía el archivo directamente al navegador. Esto se usa para varias cosas, incluido el envío de archivos FLV desde fuera de la webroot a un reproductor de video Flash incrustado.

Sin embargo, esto no parece ser un método confiable. En particular, hay un problema extraño con Internet Explorer (7), donde el navegador simplemente se cuelga después de ver un video o dos. Hacer clic en cualquier enlace, etc. no tiene ningún efecto, y la única manera de hacer que las cosas funcionen nuevamente en el sitio es cerrar el navegador y volver a abrirlo.

Esto también ocurre en otros navegadores, pero con mucha menos frecuencia. En base a algunas pruebas básicas, sospecho que esto tiene que ver con la forma en que se transmiten los archivos ... tal vez la conexión no se cierra correctamente, o algo así.

Después de probar algunas cosas diferentes, he encontrado que el siguiente método funciona para mí:

Response.WriteFile(path); 
Response.Flush(); 
Response.Close(); 
Response.End(); 

Esto consigue solucionar el problema mencionado anteriormente, y la visualización de los videos ya no hace que Internet Explorer se cuelgue.

Sin embargo, tengo entendido que Response.WriteFile() primero carga el archivo en la memoria, y dado que algunos archivos que se transmiten podrían ser bastante grandes, esto no parece una solución ideal.

Me interesa saber cómo otros desarrolladores están transmitiendo grandes archivos en ASP.NET y, en particular, reproduciendo archivos de video FLV.

+0

Este es un enfoque que utilicé el cual agrega funcionalidad de descarga reanudable que sería útil si se está transmitiendo video: http://stackoverflow.com/a/6475414/222748 – Michael

Respuesta

8

Intente abrir el archivo como una secuencia y luego use Response.OutputStream.Write(). Por ejemplo:

Edit: Malo, olvidé que Write toma un búfer de bytes. Fijo

byte [] buffer = new byte[1<<16] // 64kb 
int bytesRead = 0; 
using(var file = File.OpenRead(path)) 
{ 
    while((bytesRead = file.Read(buffer, 0, buffer.Length)) != 0) 
    { 
     Response.OutputStream.Write(buffer, 0, bytesRead); 
    } 
} 
Response.Flush(); 
Response.Close(); 
Response.End(); 

Edición 2: ¿Usted intentó esto? Deberia de funcionar.

+0

Gracias, pero no creo que eso funcione ... Respuesta. OutputStream.Write no toma un parámetro Stream (no en ASP.NET 2.0 de todos modos), solo una matriz de bytes, que requeriría que el archivo se cargue primero en la memoria y, por lo tanto, no sea realmente mejor que la solución WriteFile. – Mun

+0

Sí, no estaba prestando atención cuando escribí el código. Corregido – Randolpho

+2

Con respecto a la matriz de bytes, no es necesario cargar toda la secuencia en la memoria. El código que tengo usa como mucho 64kb de memoria (más una sobrecarga menor) durante la transmisión. – Randolpho

51

Me gustaría llevar cosas fuera de la tubería "aspx". En particular, escribiría un controlador de ejecución (ashx, o asignado a través de config), que hace el trabajo mínimo, y simplemente escribe la respuesta en fragmentos. El manejador aceptaría la entrada de query-string/form como de costumbre, ubicaría el objeto en la transmisión y transmitiría los datos (utilizando un búfer local de tamaño moderado en un bucle). Un simple (incompleta) ejemplo que se muestra a continuación:

public void ProcessRequest(HttpContext context) { 
    // read input etx 
    context.Response.Buffer = false; 
    context.Response.ContentType = "text/plain"; 
    string path = @"c:\somefile.txt"; 
    FileInfo file = new FileInfo(path); 
    int len = (int)file.Length, bytes; 
    context.Response.AppendHeader("content-length", len.ToString()); 
    byte[] buffer = new byte[1024]; 
    Stream outStream = context.Response.OutputStream; 
    using(Stream stream = File.OpenRead(path)) { 
     while (len > 0 && (bytes = 
      stream.Read(buffer, 0, buffer.Length)) > 0) 
     { 
      outStream.Write(buffer, 0, bytes); 
      len -= bytes; 
     } 
    } 
} 
+1

Un voto a la baja sin una razón no ayuda a nadie. Pensé que tu respuesta fue buena. +1 para regresar a la paridad. – Guy

+0

No soy el que votó negativamente, pero pensé que señalaría que la pregunta inicial en realidad dice que el código se está usando en un manejador de http ... Sin embargo, gracias por la sugerencia. – Mun

+1

Para cambiar el nombre del archivo, después de la línea: 'context.Response ...' agregue esta línea: 'context.Response.AppendHeader (" content-Disposition "," attachment; filename = "+ filename);' – MaciejLisCK

10

Tome un vistazo al siguiente artículo Tracking and Resuming Large File Downloads in ASP.NET que le dará más en profundidad que acaba de abrir un arroyo y patitas en la calle todos los bits. El protocolo http admite solicitudes de byte a intervalos y descargas reanudables, y muchos clientes de transmisión (como reproductores de video o Adobe pdf) pueden intentarlo y lo harán, ahorrando ancho de banda y brindando a los usuarios una mejor experiencia.

No es trivial, pero es un tiempo bien empleado.

3

Después de probar muchas combinaciones diferentes, incluido el código publicado en las distintas respuestas, parece que configurar Response.Buffer = true antes de llamar a TransmitFile hizo el truco y la aplicación web ahora es mucho más receptiva en Internet Explorer.

En este caso particular, la extensión SWF también se asigna a ASP.NET, y estamos usando un controlador personalizado en nuestra aplicación web para leer los archivos del disco y luego enviarlos al navegador utilizando Response.TransmitFile () Tenemos un reproductor de video basado en flash para reproducir archivos de video que también son archivos SWF, y creo que tener toda esta actividad pasando por el controlador sin almacenamiento en memoria intermedia es lo que puede haber causado cosas extrañas en IE.

Cuestiones relacionadas