Listar archivos en ASP .NET con VS 2022 (C# y VB)

Mostrar en el navegador web los archivos de un directorio remoto o local con una página dinámica aspx usando System.Collection o System.IO, en C# y en VB. ASP .NET tiene varias formas de acceder a los archivos y subcarpetas que están dentro de una carpeta. En este ejercicio se muestran dos maneras diferentes de listar estos elementos, ambas se basan en código relativamente sencillo.

Usando colecciones y la interfaz System.Collection.IEnumerator

Una colección (Collection) es un tipo especial de matriz o array especialmente preparado para unas tareas determinadas. Los objetos Collection se crean desde las clases e interfaces del espacio de nombres System.Collections. Algunas interfaces útiles de Collections son:

  • IEnumerator: para recorrer listas de valores
  • IList: para ordenar listas de valores
  • ICollection: para modificar listas de valores
  • ICloneable: para hacer copias de listas de valores.

En este ejercicio se usa la interfaz IEnumerator (que admite una iteración simple a través de una colección) y el método GetEnumerator (que devuelve un enumerador que puede iterar por una colección). IEnumerator es la interfaz base de todos los enumeradores. Los enumeradores sólo permiten la lectura de datos de la colección y no se pueden utilizar para modificar la colección (acceden a las matrices en modo de sólo lectura).
Inicialmente, el enumerador se coloca delante del primer elemento de la colección y mediante sus métodos podemos movernos por la lista:

  • Reset devuelve el enumerador a la posición inicial (delante del primer elemento)
  • MoveNext desplaza el enumerador al primer elemento antes de leer el valor de Current
  • Current devuelve el mismo objeto hasta que se llama a MoveNext o a Reset
  • MoveNext establece Current en el siguiente elemento.

Alcanzado el final, el enumerador se coloca después del último elemento. Un enumerador no tiene acceso exclusivo a la colección, ésto ha de ser tenido en cuenta en cuanto a la seguridad de los procedimientos (no proporciona gran seguridad). En el código de ejemplo se observa cómo se crea un array o lista para contener los nombres de los archivos del directorio pasado en una variable de cadena y también se crea un enumerador que itera a lo largo de los elementos de la lista; se puede obtener fácilmente el número de archivos del directorio contando el número de elementos de la lista (i = lista1.Length).

El objeto Response permite interaccionar servidor con cliente, su método Write envía resultados HTTP al navegador web cliente. Aquí lo utilizamos para ir enviando código HTML que va formateando una tabla cuyas celdas se irán rellenando con texto y con los resultados de la ejecución de código C#, aprovechando para formatear el aspecto del documento HTML gracias a múltiples Response.Write:

  public void Listar1(System.Object sender, System.EventArgs e)
    {
        try
        {
            Response.Write("<h2 align=center style='font-family:verdana;'>Listar archivos de un directorio</h2>");
            //array de cadenas para contener los nombres de los archivos del directorio
            string[] lista1;
            lista1 = System.IO.Directory.GetFiles(ruta);
            //objeto IEnumerator que permitirá recorrer la lista de archivos
            System.Collections.IEnumerator lista2 = default(System.Collections.IEnumerator);
            //lista2 se forma desde el método GetEnumerator de lista1
            //GetEnumerator construye un objeto enumerador que contiene:
            // -  el número de versión de la colección
            // - una referencia a los elementos de la colección
            lista2 = lista1.GetEnumerator();
            //i: entero para contener el número de archivos del directorio
            //j: entero para contener la longitud de la cadena ruta
            i = lista1.Length;
            j = ruta.Length;
            //crear la tabla en cuyas celdas aparece la información
            //se usan estilos CSS para dar formato a las celdas
            Response.Write("<table align=center border=0'>");
            Response.Write("<tr style='font-family:verdana; font-size:10pt; font-weight:bold; text-align:center;background-color:black; color:white;'><td>");
            //cabecera de la tabla con el nombre del directorio
            Response.Write("<b>Directorio: " + ruta2 + "</b>");
            Response.Write("</td></tr>");
            Response.Write("<tr style='font-family:verdana; font-size:10pt; text-align:center;background-color:blue; color:white;'><td>");
            //fila con el número de archivos del directorio
            Response.Write("Número de archivos: <b>" + System.Convert.ToString(i) + "</b>");
            Response.Write("</td></tr>");
            //se van leyendo los elementos de la colección hasta el final de la misma
            while (lista2.MoveNext())
            {
                Response.Write("<tr style='font-family:verdana; font-size:10pt; text-align:left;background-color:Gainsboro; color:black;'><td>");
                //lista.Current devuelve el nombre largo de archivo (con ruta incluida)
                //para dejar sólo el nombre corto de archivo (sin la ruta):
                // - si la variable ruta termina en \ se quita de lista.Current la longitud de la variable
                //(ruta termina en \ si se forma con ServerMapPath (ej: ruta = Server.MapPath("/img"))
                // - si la variable ruta NO termina en \ se quita de lista.Current la longitud de la variable más 1 carácter
                //(ruta NO termina en \ si se forma con la ruta como cadena sencilla (ej: ruta="C:\Windows")
                if (ruta.EndsWith("\\"))
                {
                    Response.Write(System.Convert.ToString(lista2.Current).Remove(0, j));
                }
                else
                {
                    Response.Write(System.Convert.ToString(lista2.Current).Remove(0, j + 1));
                }
                Response.Write("</tr></td>");
            }
            Response.Write("</table>");
        }
        catch (Exception pollo)
        {
            if (texto.Text.Length == 0)
            {
                Response.Write("<p align=center style='font-family:verdana; font-size:10pt; color:red;'><b>" +
                    "No se ha definido la ruta a los archivos que se van a listar.</b></p>");
            }
            else
            {
                Response.Write("<p align=center style='font-family:verdana; font-size:10pt; color:red;'><b>" +
                    pollo.Message + "</b></p>");
            }
            Response.End(); //detiene la carga de la página
        }
        Response.End(); //detiene la carga de la página
}


Usando File, Directory, FileInfo y DirectoryInfo de System.IO

El espacio de nombres System.IO contiene clases que facilitan el trabajo con archivos y carpetas:

  • File y Directory: contienen métodos estáticos. Los miembros de File y de Directory son miembros compartidos por lo que es más sencillo su uso pues no es necesario crear una instancia previa de su clase; sirven para crear, borrar y modificar archivos y carpetas: Exists, Copy, Move, Delete, OpenText, GetAttributes
  • FileInfo y DirectoryInfo: contienen métodos de instancia. por lo que es necesario crear un objeto para poder utilizar la clase; sus métodos sirven para extraer información de los atributos de archivos y carpetas: Name, FullName, CreationTime, etc…
  • Path: contiene métodos para administrar cadenas DirectoryInfo / archivo.

En este ejercicio se utilizan las clases FileInfo y DirectoryInfo junto a matrices para contener los nombres de las carpetas o archivos, para mostrar al usuario un listado de archivos contenidos en el directorio seleccionado y en los subdirectorios de primer nivel, de entre los que componen el sitio web del que forma parte esta aplicación. Es muy sencillo retocar el código para que muestre archivos de directorios locales del disco duro fuera de las carpetas que componen el servidor web pero para ello hay que tener permisos de acceso a esas carpetas, por lo que puede no funcionar sirviendo estas páginas desde Internet (el proveedor de servicios tal vez no lo permita).

Mediante múltiples Response.Write y usando bucles For Each que recorren el directorio especificado en la ruta se va construyendo una tabla en la que se muestra la lista de archivos, clasificados por la carpeta contenedora, informando de su tamaño y fecha de creación y contando su número (por carpeta y el total).
Un array de String contiene los nombres de los archivos que hay en el directorio (pasado como argumento al método GetFiles de la clase Directory). Contando el número de elementos de ese array sabemos el número de archivos del directorio.
Se necesitan objetos del tipo FileInfo y DirectoryInfo para poder extraer información de archivos y carpetas.

Con un bucle For Each y un objeto de tipo FileInfo obtenemos la lista de archivos del directorio y extraemos información de cada archivo (nombre, tamaño y fecha de creación, por ejemplo). Con un segundo bucle For Each y un objeto DirectoryInfo obtenemos la lista de carpetas del directorio y extraemos el nombre de cada carpeta, para listar los archivos de cada carpeta con un tercer For Each y otro objeto de tipo FileInfo (ésto permite listar los archivos contenidos en el directorio elegido y en los subdirectorios de primer nivel).

//listar los archivos de un directorio y del primer subnivel de directorios
public void Listar3(System.Object sender, System.EventArgs e)
{
    string archivo = ""; //para el nombre de archivos y carpetas
    string carpeta = "";
    string[] sArchivos; //array con los nombres de archivos y carpetas
    string[] sCarpetas;
    DirectoryInfo carpetaInfo = default(DirectoryInfo); //objeto para extraer propiedades de las carpetas
    FileInfo archivoInfo = default(FileInfo); //objeto para extraer propiedades de los archivos
    try
    {
        //el objeto Response permite interaccionar servidor con cliente,
        //su método Write envía resultados HTTP al navegador web cliente.
        //Aquí lo utilizamos para ir enviando código HTML que va formateando una tabla
        //cuyas celdas se irán rellenando con texto y resultados de la ejecución de código VB
        Response.Write("<h2 align=center style='font-family:verdana;'>Listar archivos de un directorio</h2>");
        //array con los nombres de archivo en el directorio actual
        sArchivos = Directory.GetFiles(ruta);
        sCarpetas = Directory.GetDirectories(ruta);
        //número de archivos en el directorio
        i = sArchivos.Length;
        //crear la tabla y la cabecera con títulos de columnas
        Response.Write("<table align=center border=0 style='font-family:verdana; font-size:10pt;'>");
        Response.Write("<tr style='background-color:black; color:white; font-weight:bold;'>");
        Response.Write("<td>Nombre</td>");
        Response.Write("<td align=right>Tamaño</td>");
        Response.Write("<td align=center>Fecha</td>");
        Response.Write("<td align=center>Hora</td></tr>");
        //sección que lista los archivos que cuelgan directamente del directorio actual
        //condiciones: listar sólo en carpetas con al menos 1 archivo
        if (i > 0)
        {
            Response.Write("<tr><td colspan=4 align=center style='background-color:gray; color:white;'>");
            Response.Write("<b>Directorio " + ruta2 + " [archivos: " + System.Convert.ToString(i) + "</b>]");
        }
        Response.Write("</td></tr>");

        //Obtener lista de archivos contenidos en el directorio actual
        foreach (string tempLoopVar_archivo in sArchivos)
        {
            archivo = tempLoopVar_archivo;
            archivoInfo = new FileInfo(archivo);
            Response.Write("<tr>");
            Response.Write("<td bgcolor=Gainsboro>" + archivoInfo.Name + "</td>");
            Response.Write("<td bgcolor=Gainsboro align=right>" + archivoInfo.Length.ToString("#,#") + " bytes</td>");
            Response.Write("<td bgcolor=Gainsboro align=center>" + archivoInfo.CreationTime.ToShortDateString() + "</td>");
            Response.Write("<td bgcolor=Gainsboro align=center>" + archivoInfo.CreationTime.ToLongTimeString() + "</td></tr>");
        }

        //Obtener lista de directorios del directorio actual
        foreach (string tempLoopVar_carpeta in sCarpetas)
        {
            carpeta = tempLoopVar_carpeta;
            //For Each carpeta In Directory.GetDirectories(ruta)
            carpetaInfo = new DirectoryInfo(carpeta);
            //array con nombres de archivos en cada directorio
            sArchivos = Directory.GetFiles(carpeta);
            //número de carpetas en el directorio
            j = sArchivos.Length;
            //sección que lista las carpetas que cuelgan directamente del directorio
            //condición: listar sólo en carpetas con al menos 1 archivo
            //If j > 0 Then
            Response.Write("<tr><td colspan=4 align=center style='background-color:gray; color:white;'>");
            //condición: ocultar algunos directorios que no queremos que aparezcan en la lista
            //If carpetaInfo.Name <> "nombre_de_carpeta_para_ocultar" Then
            if (ruta2 == "/")
            {
                Response.Write("<b>Directorio " + ruta2 + carpetaInfo.Name + " [archivos: " + System.Convert.ToString(j) + "</b>]");
            }
            else if (ruta2.StartsWith("C:\\"))
            {
                Response.Write("<b>Directorio " + ruta2 + "\\" + carpetaInfo.Name + "[archivos: " + System.Convert.ToString(j) + "</b>]");
            }
            else
            {
                Response.Write("<b>Directorio " + ruta2 + "/" + carpetaInfo.Name + " [archivos: " + System.Convert.ToString(j) + "</b>]");
            }
            //End If
            //End If
            Response.Write("</td></tr>");
            //calcular el número total de archivos que hay en los directorios
            i += j;
            //obtener lista de archivos contenidos en los directorios de primer nivel
            //sección que lista los archivos de esos directorios
            foreach (string tempLoopVar_archivo in Directory.GetFiles(carpeta))
            {
                archivo = tempLoopVar_archivo;
                archivoInfo = new FileInfo(archivo);
                Response.Write("<tr bgcolor=Gainsboro><td>/" + carpetaInfo.Name + "/" + archivoInfo.Name + "</td>");
                Response.Write("<td bgcolor=Gainsboro align=right>" + archivoInfo.Length.ToString("#,#") + " bytes</td>");
                Response.Write("<td bgcolor=Gainsboro>" + archivoInfo.CreationTime.ToShortDateString() + "</td>");
                Response.Write("<td bgcolor=Gainsboro>" + archivoInfo.CreationTime.ToLongTimeString() + "</td></tr>");
            }
        }

    }
    catch (Exception pollo)
    {
        //'finalizar la tabla con el Nº total de archivos listados
        //'sólo si el TextBox contiene alguna ruta
        //If ruta2 <> "" Then
        //    Response.Write("<tr><td colspan=4 align=center style='background-color:blue; color:white;'>")
        //    Response.Write("<b>Número total de archivos = " & i & "</b>")
        //    Response.Write("</td></tr>")
        //End If
        Response.Write("</table>");
        if (texto.Text.Length == 0)
        {
            Response.Write("<p align=center style='font-family:verdana; font-size:10pt; color:red;'><b>" +
                "No se ha definido la ruta a los archivos que se van a listar.</b></p>");
        }
        else
        {
            Response.Write("<p align=center style='font-family:verdana; font-size:10pt; color:red;'><b>" +
                pollo.Message + "</b></p>");
        }
        Response.End(); //detiene la carga de la página
    }
    //finalizar la tabla con el Nº total de archivos listados
    Response.Write("<tr><td colspan=4 align=center style='background-color:blue; color:white;'>");
    Response.Write("<b>Número total de archivos = " + System.Convert.ToString(i) + "</b>");
    Response.Write("</td></tr>");
    Response.Write("</table>");
    Response.End(); //detiene la carga de la página
}


Código común a ambos métodos

La página aspx presenta al usuario un cuadro de texto en el que se introduce la ruta a una carpeta del servidor web (incluido el directorio raíz /) o a una carpeta del disco duro, teniendo en cuenta que, ejecutada desde Internet, esta aplicación intenta acceder a contenidos que, dependiendo de la configuración del servidor web, es posible que no sean accesibles por no tener permisos suficientes para ello; es recomendable descargar la aplicación a nuestro disco duro y probarla desde ahí. Se necesitan variables string para los nombres de archivos y carpetas y arrays para contener las listas de archivos y carpetas y poder contarlos.

Se crea el título de la página web en tiempo de ejecución, al cargar la página:

private void Page_Load(System.Object sender, System.EventArgs e)
{
	//configurar el título del documento HTML en tiempo de ejecución
	Response.Write("<HEAD><TITLE>Listado de archivos</TITLE></HEAD>");
}

La propiedad text del cuadro de texto se pasa, en el código C#, a una variable String que ayuda a construir la ruta hacia el directorio usando Server.MapPath().

protected string ruta;
protected string ruta2;
//ruta2 se asigna al ítem elegido
ruta2 = texto.Text;
//la ruta se construye de manera diferente si se usa:
// - AppDomain.CurrentDomain.BaseDirectory que nos da la ruta física que se corresponde
//con la ruta virtual de la aplicación ASP NET
// - una cadena simple como ruta física a un directorio del disco duro
//En las rutas virtuales del servidor web se usa Server.MapPath y se le pasa como parámetro la cadena ruta2
if (ruta2.StartsWith("/") || ruta2.Contains("/"))
{
     ruta = Server.MapPath(ruta2);
     //ruta = AppDomain.CurrentDomain.BaseDirectory & ruta2
     Response.Write("<div align=center style='font-family:verdana; font-size:10px; color: darkred'>");
     //Response.Write("Server.MapPath(ruta2): " + Server.MapPath(ruta2));
     Response.Write("<br>BaseDirectory: " + AppDomain.CurrentDomain.BaseDirectory.ToString());
     Response.Write("<br>Lista de archivos en: " + ruta2 + "</div>");
}
else
{
//en los demás casos (rutas reales al disco duro o cadena vacía) se igualan ruta y ruta2
     ruta = ruta2;
}


Código para descargar la aplicación

Por último, se propone una manera diferente a la habitual para descargar el archivo comprimido zip. El método clásico mediante código HTML es conocido:

<A HREF="archivos.zip"></A>

En este ejercicio en la página aspx se asocia el evento ONCLICK de un botón con el método Zip:

<asp:Button ID="bt5" runat="server" CssClass="bt" Text="App en VB y C# para Visual Studio 2022" OnClick="Zip"></asp:Button>    

En el código C# se desarrolla el método Zip usando Response.Redirect para descargar el archivo:

//descargar el archivo zip con la aplicación completa
public void Zip(System.Object sender, System.EventArgs e)
{
    Response.Redirect("archivos.zip", true);
}

Puedes descargar un archivo ZIP con las aplicaciones C# y VB.

Deja un comentario