Cómo leer archivos planos con ADO.NET (versión Visual Basic 2005)

jueves 13 de diciembre de 2007 | categorías: , , , , | 4 comentarios -- da clic aquí para dejar el tuyo

Hace poco más de un año escribí este artículo que describe una técnica para leer archivos planos utilizando el OleDB provider de ADO.NET.  Es uno de los artículos de este sitio que ha recibido más comentarios, y entre ellos está uno que dejó fredy que me hizo re-hacer el ejemplo en Visual Basic 2005 para comprobar que no fuera un error de código—en realidad él hizo la mayor parte de la chamba para "traducir" la rutina.

No voy a explicar mucho la lógica del código—para eso te dejo de tarea que leas el artículo original—aquí simplemente te comparto cómo se vería la rutina en VB:

Imports System.IO
Imports System.Data
Imports System.Data.OleDb
 
Module Utilerias
   Public Enum TipoDeArchivoPlano
        Delimited
        Fixed
   End Enum
 
   Public Function LeerArchivoPlano(ByVal archivo As FileInfo, _
        ByVal tieneEncabezado As Boolean, _
        ByVal tipoDeArchivo As TipoDeArchivoPlano) As DataTable
 
        If (Not archivo.Exists) Then
            Throw New FileNotFoundException("No se encontró el archivo especificado")
        End If
 
        Dim conEncabezado As String = IIf(tieneEncabezado, "YES", "NO")
 
        Dim connectionString As String = _
            String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};" + _
            "Extended Properties='text;HDR={1};FMT={2}'", _
            archivo.DirectoryName, conEncabezado, tipoDeArchivo.ToString())
 
        Dim dt As DataTable = New DataTable("miTabla")
 
        Using conn As OleDbConnection = New OleDbConnection(connectionString)
            Using da As OleDbDataAdapter = New OleDbDataAdapter( _
               "SELECT * FROM " + archivo.Name, conn)
 
               da.Fill(dt)
            End Using
        End Using
 
        Return dt
   End Function
End Module

Para probarla, hice una aplicación sencilla en ASP.NET que mostrara los datos de un archivo .CSV que está dentro de un subdirectorio del sitio web.

El archivo jason.csv contiene:

Producto,Cantidad,Precio
Sierra eléctrica,1,250
Máscara de hockey,1,15.50
Machete,5,2.70
Detergente para ropa (con quita-manchas),1,10
Delantal,2,7.25
Afilador,3,5

La página dentro de la solución que en realidad solo tiene un GridView.  Este es el contenido de Default.aspx:

<%@ Page Language="VB" AutoEventWireup="false" 
   CodeFile="Default.aspx.vb" 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>Leer un archivo plano con VB 2005</title>
</head>
<body>
   <form id="miFormulario" runat="server">
   <div>
        <asp:GridView ID="miGridView" runat="server">
        </asp:GridView>
   </div>
   </form>
</body>
</html>

Finalmente, para mandar llamar la rutina y bindear—¿enlazar?—los datos al GridView, solo agregué esto en el code-behind:

Partial Class _Default
   Inherits System.Web.UI.Page
 
   Protected Sub miFormulario_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles miFormulario.Load
 
        If Not Page.IsPostBack Then
            Dim archivo As FileInfo = _
               New FileInfo("D:\WebSites\LeerArchivosPlanosVB\Archivos\jason.csv")
 
            Dim tabla As New DataTable
            tabla = Utilerias.LeerArchivoPlano(archivo, True, TipoDeArchivoPlano.Delimited)
 
            If tabla.Rows.Count > 0 Then
               miGridView.DataSource = tabla
               miGridView.DataBind()
            Else
               Response.Write("No hay datos para mostrar.")
            End If
 
        End If
   End Sub
End Class

El resultado de correr la página:

 

Whew! Funcionó smile_teeth

Enjoy. smile_shades


PD.  Puedes descargar el código de este ejemplo del sitio de la Comunidad .NET de Cd. Juárez.

Dynamic Data se ve interesante (y con screencast se entiende mejor)

miércoles 12 de diciembre de 2007 | categorías: , , | 0 comentarios -- da clic aquí para dejar el tuyo

Dynamic Data es uno de los features de las ASP.NET 3.5 Extensions (aún en Preview) que me llamó la atención el otro día.  Te permite hacer una versión scaffolded—un armazón pues—de tu aplicación Web a partir de tu modelo de base de datos, algo al estilo de Rails.

(Ahí les encargo la traducción de “scaffolded” al español.  Hasta donde sé un scaffold es un andamio, como el que usan los maistros albañiles en la talacha, pero no creo que “andamiada” sea una palabra correcta. Y si lo es, qué fea.  A mi no me gusta nada “miado” o “miada”) smile_wink

Esto es bastante útil porque muchas veces necesitas arrancar con algo para poder meter datos aunque sea de prueba a tu base de datos.  Y muchas veces esto, con cambios muy menores, es suficiente para lo que los usuarios necesitan de la aplicación.  Si tienes una base de datos bien armada—y sí la tienes ¿verdad?—o al menos tu modelo de datos bien armado, esto ya te quitó esa talacha.

Ayer precisamente estaba intentando explicarle a un compañero del trabajo qué era Dynamic Data: “Pos ¿ya ves cómo está shida que puedes nomás arrastrar un SqlDataSource y un GridView a una página ASP.NET y sin mucho esfuerzo ya puedes editar tu tabla?  Bueno pues esto es como eso, pero para toda tu base de datos, si así lo quisieras. Y como está basado en templetes, pues puedes alterar de un sablazo todo el sitio...”

Pero al parecer no supe explicarlo muy bien, así que después de la cara de guatdefuc que me puso, me dió mucho gusto mostrarle este screencast (en inglés) que me encontré hoy en el blog de David Ebbo:

I made a screencast which walks through a basic scenario of using ASP.NET Dynamic Data in a simple site using Northwind.  It's about 17 minutes long.  Enjoy and feel free to give any feedback here or in the forum.

Enjoy. smile_shades

Cómo subir un archivo a tu servidor web mediante el control FileUpload de ASP.NET

jueves 6 de diciembre de 2007 | categorías: , , , | 10 comentarios -- da clic aquí para dejar el tuyo

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:

Una forma ASP.NET para subir un archivo a tu servidor web

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.

 Permisos necesarios para que ASP.NET pueda subir (escribir) archivos en el servidor

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... smile_baringteeth smile_wink 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. smile_shades