Otra pregunta muy común entre los programadores principantes es de qué manera pueden enviar al navegador de sus usuarios desde una página ASPX los contenidos de un archivo que está en el servidor.

La mayoría conoce el método Write de la clase HttpResponse que se llama desde una página simplemente escribiendo:

Response.Write("Hola desde mi página ASPX");

que lo que hace es introducir en el código de la página actual ese mensaje. Este método no se suele utilizar ya que introduce el texto fuera del flujo normal de renderizado de la página y por lo tanto suele quedar al principio de la página, descolgado. Se incorporó más por compatibilidad con ASP 3.0 clásico que por otra cosa.

Sin embargo esto devuelve texto únicamente al cliente. ¿Qué pasa si queremos enviar contenidos binarios, como los de un archivo PDF o un DOCX?

Tipos MIME

Veamos un concepto básico de Internet explicado de manera somera para los propósitos que nos ocupan ahora: el envío de archivos.

El  protocolo en el que se basa la Web es HTTP (HyperText transfer Protocol). HTTP está basado en texto, es decir, que sólo soporta el trasiego de información en formato textual, y por lo tanto en teoría no soportaría la transferencia de archivos binarios. Sin embargo todos los días nos descargamos sin problemas a través de la Web archivos ejecutables, PDFs, etc... que son todos archivos binarios ¿cómo es posible?

Las extensiones MIME nacieron con el objetivo de aumentar la capacidad de codificación del correo electrónico. Éste soportaba únicamente los caracteres ASCII americanos (de 7 bits), claramente insuficientes para soportar, no ya datos binarios, sino los caracteres de muchos idiomas occidentales y por supuesto asiáticos. MIME ofrece formas de codificar otros datos diferentes a ASCII de manera que sean simples caracteres ASCII, por lo que de este modo se puede enviar por email cualquier tipo de información que deseemos. Si bien su origen está en el e-mail, las extensioes MIME se utilizan en todo Internet desde hace muchos años. De estas extensiones derivan los llamados tipos MIME o tipos de contenido. Se trata de identificadores para determinar el tipo de un contenido transferido a través de un protocolo de Internet.

Gracias a MIME se pueden transferir datos binarios a través de HTTP y con los tipos MIME el navegador sabe cómo interpretar un contenido que recibe y que no es una página HTML. ¿Nunca te ha pasado que intentas leer un PDF en una web por ahí y te salen muchos caracteres extraños en el navegador en vez de ver el PDF? Pues eso es porque el servidor no envía al navegador la cabecera apropiada indicando el tipo MIME del archivo.

Ya he hablado en otras ocasiones de problemas al descargar archivos debido a una mala configuración de tipos MIME: ¿Por qué hay ciertos tipos de archivos que no somos capaces de descargar de nuestro servidor Web?

Por todo esto es esencial que, antes de enviar un archivo desde nuestro código de servidor, establezcamos una cabecera HTTP especial llamada content-type que sirve para indicar el tipo MIME del archivo que vamos a enviar a continuación. Si no lo hacemos corremos el riesgo de que pase lo que acabo de describir.

En .NET es tan sencillo como escribir esto en el manejador o página que creemos:

Response.ContentType = "image/jpg";

siendo la cadena el tipo MIME que vamos a indicar, en este caso el correspondiente a una imagen en formato JPEG.

Cada tipo de archivo tiene su tipo MIME correspondiente. Por ejemplo el de los PDF es "application/pdf", el de las hojas Excel 2007 (extensión .xlsx) es "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" o el de las Excel anteriores (.xls) es "application/vnd.ms-excel".

La IANA, el organismo encargada entre muchas otras cosas de estandarizar estos tipos, tiene una lista de tipos MIME estándar, y se interesan específicamente los de Microsoft Office en esta página tienes una lista muy completa.

Enviar el archivo

Vale, ya sabemos que hay que establecer el tipo MIME enla cabecera para que todo funciones, pero ¿cómo enviamos ahora el contenido del archivo al navegador?

Podemos usar el método BinaryWrite de la misma clase HttpResponse de la uqe hemos hablado antes. Éste permite escribir datos binarios directamente en la respuesta de la petición actual. Así, por ejemplo, podríamos leer el archivo desde disco y usar BinaryWrite para enviar sus contenidos en la respuesta.

No obstante la clase HttpResponse dispone de un método especial más directo y apropiado que nos permite enviar archivos directamente al cliente desde la propia página, control, controlador o manejador (estos últimos son los más recomendables para enviar archivos). Se trata de WriteFile. Este método tiene cuatro sobrecargas, pero lo habitual es que usemos la primera de ellas, que simplemente toma como parámetro la ruta en el disco del servidor del archivo que queramos enviar al cliente. Por ejemplo:

Response.WriteFile(Server.MapPath("docs/informe.pdf"));

Esto transforma la ruta virtual en una ruta física con el método MapPath del servidor (así tendremos algo en la forma C:\MiCarpeta\Docs\informe.pdf), y luego se envía al cliente con WriteFile.

Más sencillo imposible.

Archivos grandes

Esto está muy bien para enviar archivos de tamaño relativamente pequeño. Dependerá del hardware de tu servidor pero, aunque con esto podrás enviar archivos de varios megas sin problemas, si los archivos son realmente grandes (cientos de megas) y tu servidor está muy saturado, el método empezará a devolver errores porque nos quedaremos sin memoria para que sea operativo.

Esto es un problema reconocido por Microsoft y, de hecho, es también fácil de solucionar, aunque un poco más tedioso. La solución consiste, como ya apuntaba al principio, en enviar el archivo en trozos más pequeños que se van leyendo y descartando.

En este artículo de la Knowledge Base de Microsoft:

PRB: Response.WriteFile cannot download a large file

se explica cuál es este problema y se da código de ejemplo tanto en VB como en C# de cómo realizar este envío troceado del que estoy hablando.

Espero que te resulte útil.

Posts relacionados en este blog:

· Problemas subiendo archivos al servidor con ASP.NET

Escrito por un humano, no por una IA