Este es un tip súper sencillo, pero bastante útil, y como ya van varias personas que me lo preguntan, mejor lo explico aquí.
A menudo se ofrece que los usuarios de nuestra aplicación web nos envíen (o "suban") algún archivo al servidor web donde corre nuestra aplicación ASP.NET. Pudiera ser que el gerentoide tiene un archivo de texto o .CSV que necesitamos leer para cargarlo en la base de datos, o una imágen que vamos a poner como avatar en el perfil del usuario, qué se yo.
Con ASP.NET 2.0 o posterior, subir un archivo a tu servidor es sencillísimo mediante el control FileUpload. Supongamos que tenemos una forma de ASP.NET como esta:
El markup sería el siguiente:
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Subir un archivo</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h3>
Cómo subir un archivo a tu servidor web mediante ASP.NET</h3>
<p>
Elige el archivo a subir:
<asp:FileUpload ID="trepador" runat="server" />
</p>
<p>
<asp:Button ID="subir" runat="server"
Text="Subir Archivo" OnClick="subir_Click" />
</p>
<p>
<asp:Label ID="estatus" runat="server"
Style="color: #0000FF"></asp:Label>
</p>
</div>
</form>
</body>
</html>
Como puedes ver la forma en realidad consiste de únicamente de un control FileUpload, un botón para iniciar el postback, y un Label que usaré para mostrar un mensaje. Al botón le asociamos un manejador para el evento Click—que veremos un poquito más abajo.
Las propiedades y métodos relevantes del control FileUpload son:
- SaveAs(), que se usa para decir dónde quieres guardar el archivo que se está subiendo. Debes proporcionar la ruta completa y el nombre.
- HasFile, que indica si el usuario ya escribió el nombre de un archivo o lo seleccionó mediante el botón "Browse..."
Ahora, este es el código para manejar el evento:
using System;
using System.IO;
public partial class _Default : System.Web.UI.Page
{
protected void subir_Click(object sender, EventArgs e)
{
if (trepador.HasFile)
{
string directorio = @"C:\Archivos de Usuario\";
if (Directory.Exists(directorio))
{
string archivo = directorio + trepador.FileName;
if (File.Exists(archivo))
{
// ya existe un archivo con el mismo nombre en el directorio,
// así que hay hacer algo al respecto (p.ej. renombrar el que
// está en el servidor o asignarle otro nombre al que se está
// subiendo), de lo contrario el archivo en el servidor será
// sobreescrito
}
else
{
trepador.SaveAs(archivo);
estatus.Text = "Tu archivo ha sido enviado exitosamente.";
//
// TODO: código para procesar el archivo va aquí...
//
}
}
else
{
throw new DirectoryNotFoundException(
"El directorio en el servidor donde se suben los archivos no existe");
}
}
}
}
Consideraciones adicionales
Hay dos aspectos más que debes cuidar con esto de la "trepada" de archivos.
Primero, debes asegurarte que la cuenta de usuario bajo la que corre el ASP.NET Worker Process (típicamente la cuenta se llama ASPNET) tenga suficientes permisos de seguridad sobre el directorio donde quieres escribir los archivos en el servidor, de lo contrario verás una excepción de seguridad. Creo que el permiso mínimo que necesita es de Write, aunque yo casi siempre uso el de Modify en mi máquina local.
Y segundo, ASP.NET, por default, está configurado para subir archivos de hasta 4 Mb únicamente (4096 bytes). Esto es porque la petición la recibe IIS primero y debe "tragársela" completa antes de pasársela al Worker Process. Si lo que vas a subir son archivos de texto, pues 4 Mb es un chorro, pero si es otro tipo de archivos—imágenes p0rno, archivos de Word, etcétera—pues esto podría ser terriblemente insuficiente. Si el archivo es muy grande, el otro problema es que la petición de HTTP pudiera exceder el tiempo máximo por petición y hacer timeout. Afortunadamente podemos alterar esto mediante el archivo de configuración del sitio (web.config):
<?xml version="1.0"?>
<configuration>
<system.web>
<!-- Requerido para subir archivos grandes. En este caso
especificas un tiempo máximo de 10 minutos (600 segundos)
y un tamaño máximo de archivos de 50Mb
-->
<httpRuntime executionTimeout="600" maxRequestLength="51200"/>
</system.web>
</configuration>
En la configuración anterior exageré un bastante, y deberías tener cuidado de valores como estos, ya que estás indicando que puede haber peticiones de ASP.NET que duren hasta 10 minutos ejecutándose, y esto podría llevar a que tu servidor se sature si de pronto hay varias peticiones grandes. Para la mayoría de las aplicaciones el valor default de 110 segundos (minuto y medio) como executionTimeout es suficiente. Especificar valores grandes para el maxRequestLength, de manera similar, expone tu servidor a ser saturado, así que debes alterar estos valores solo si en verdad lo requieres, y solo para los valores máximos que se necesiten para tu aplicación.
Por último, la documentación de ASP.NET dice que se debe especificar el valor de executionTimeout debe especificarse como un TimeSpan (p.ej. "00:01:50"), pero en la práctica, no he podido utilizarlo así. Siempre he tenido que especificarlo como el número de segundos.
Conclusión
El control FileUpload hace que el problema de subir achivos a tu servidor web sea pan comido. Solo necesitas agregarlo a tu página ASPX y agregar un botón u algún otro control para que invoque la lógica necesaria para copiar el archivo a tu servidor mediante el método SaveAs(). Solo debes cuidar que la cuenta ASPNET tenga los permisos adecuados, y que la configuración del servidor sea apropiada para los tipos de archivos que vayas a subir.
Hay muchas mejoras que se le puede hacer al ejemplo de este artículo. La primera que se me ocurre a mi es proporcionar algún tipo de retroalimentación al usuario de que el archivo se está procesando, o deshabilitar el botón inmediatamente después del click para que el usuario no le de click dos veces hasta que termine de subirse el archivo—estos son problemas bastante comunes cuando quieres subir archivos grandes y ya sea que tu servidor esté algo ocupado o tu usuario sea un neurótico desesperado que no puede esperar ni los 7 segundos promedio como el resto de los seres humanos normales...
pero en fin, esa ya es harina de otro costal, y tendría que hablar de ASP.NET AJAX. Quizá en la próxima.
Enjoy. 