Carga de archivos (C#)

de Scott Mitchell

Descargar PDF

Obtenga información sobre cómo permitir que los usuarios carguen archivos binarios (como documentos Word o PDF) en el sitio web en el que se puedan almacenar en el sistema de archivos del servidor o la base de datos.

Introducción

Todos los tutoriales que hemos examinado hasta ahora han trabajado exclusivamente con datos de texto. Sin embargo, muchas aplicaciones tienen modelos de datos que capturan datos binarios y de texto. Un sitio de citas en línea podría permitir a los usuarios cargar una imagen para asociarla a su perfil. Un sitio web de contratación podría permitir que los usuarios carguen su currículum como un documento de Microsoft Word o PDF.

Trabajar con datos binarios agrega un nuevo conjunto de desafíos. Debemos decidir cómo se almacenan los datos binarios en la aplicación. La interfaz que se usa para insertar nuevos registros debe actualizarse para permitir al usuario cargar un archivo desde su equipo. Además, se deben realizar pasos adicionales para mostrar o proporcionar un medio para descargar los datos binarios asociados de un registro. En este tutorial y los tres siguientes, exploraremos cómo superar estos desafíos. Al final de estos tutoriales, habremos creado una aplicación totalmente funcional que asocia una imagen y un folleto PDF a cada categoría. En este tutorial concreto, veremos diferentes técnicas para almacenar datos binarios y exploraremos cómo permitir a los usuarios cargar un archivo desde su equipo y guardarlo en el sistema de archivos del servidor web.

Nota:

Los datos binarios que forman parte de un modelo de datos de una aplicación se conocen a veces como BLOB, un acrónimo de Binary Large OBject. En estos tutoriales, he elegido usar la terminología "datos binarios", aunque el término BLOB es un sinónimo.

Paso 1: Crear las páginas web de Trabajar con datos binarios

Antes de empezar a explorar los desafíos asociados con la adición de compatibilidad con datos binarios, primero dediquemos un momento a crear las páginas de ASP.NET en nuestro proyecto de sitio web que necesitaremos para este tutorial y los tres siguientes. Empiece agregando una nueva carpeta denominada BinaryData. Después, agregue las siguientes páginas ASP.NET a esa carpeta, asegurándose de asociar cada página a la página maestra Site.master:

  • Default.aspx
  • FileUpload.aspx
  • DisplayOrDownloadData.aspx
  • UploadInDetailsView.aspx
  • UpdatingAndDeleting.aspx

Add the ASP.NET Pages for the Binary Data-Related Tutorials

Figura 1: Agregar las páginas de ASP.NET para los tutoriales relacionados con datos binarios

Igual que en las otras carpetas, Default.aspx en la carpeta BinaryData enumerará los tutoriales en su sección. Recuerde que el control de usuario SectionLevelTutorialListing.ascx proporciona esta funcionalidad. Por lo tanto, agregue este control de usuario a Default.aspx arrastrándolo desde el Explorador de soluciones a la vista Diseño de la página.

Add the SectionLevelTutorialListing.ascx User Control to Default.aspx

Figura 2: Agregar el control de usuario SectionLevelTutorialListing.ascx a Default.aspx (haga clic aquí para ver la imagen a tamaño completo)

Por último, agregue las siguientes páginas como entradas al archivo Web.sitemap. En concreto, agregue el marcado siguiente después del <siteMapNode> Mejorar el control GridView:

<siteMapNode 
    title="Working with Binary Data" 
    url="~/BinaryData/Default.aspx" 
    description="Extend the data model to include collecting binary data.">
    
    <siteMapNode 
        title="Uploading Files" 
        url="~/BinaryData/FileUpload.aspx" 
        description="Examine the different ways to store binary data on the 
                     web server and see how to accept uploaded files from users 
                     with the FileUpload control." />
    <siteMapNode 
        title="Display or Download Binary Data" 
        url="~/BinaryData/DisplayOrDownloadData.aspx" 
        description="Let users view or download the captured binary data." />
    <siteMapNode 
        title="Adding New Binary Data" 
        url="~/BinaryData/UploadInDetailsView.aspx" 
        description="Learn how to augment the inserting interface to 
                     include a FileUpload control." />
    <siteMapNode 
        title="Updating and Deleting Existing Binary Data" 
        url="~/BinaryData/UpdatingAndDeleting.aspx" 
        description="Learn how to update and delete existing binary data." />
</siteMapNode>

Después de actualizar Web.sitemap, dedique un momento a ver el sitio web de tutoriales a través de un explorador. El menú de la izquierda ahora incluye elementos para los tutoriales de Trabajar con datos binarios.

The Site Map Now Includes Entries for the Working with Binary Data Tutorials

Figura 3: El mapa del sitio ahora incluye entradas para los tutoriales de Trabajar con datos binarios

Paso 2: Decidir dónde almacenar los datos binarios

Los datos binarios asociados al modelo de datos de la aplicación se pueden almacenar en uno de dos lugares: en el sistema de archivos del servidor web con una referencia al archivo almacenado en la base de datos, o directamente dentro de la propia base de datos (vea la figura 4). Cada enfoque tiene su propio conjunto de ventajas y desventajas y merece una discusión más detallada.

Binary Data Can Be Stored On the File System or Directly in the Database

Figura 4: Los datos binarios se pueden almacenar en el sistema de archivos o directamente en la base de datos (haga clic para ver la imagen en tamaño completo)

Imagine que queremos ampliar la base de datos Northwind para asociar una imagen a cada producto. Una opción sería almacenar estos archivos de imagen en el sistema de archivos del servidor web y registrar la ruta de acceso en la tabla Products. Con este enfoque, agregaríamos una columna ImagePath a la tabla Products de tipo varchar(200), quizá. Cuando un usuario carga una imagen para Chai, esa imagen podría almacenarse en el sistema de archivos del servidor web en ~/Images/Tea.jpg, donde ~ representa la ruta de acceso física de la aplicación. Es decir, si el sitio web se basa en la ruta de acceso física C:\Websites\Northwind\, ~/Images/Tea.jpg sería equivalente a C:\Websites\Northwind\Images\Tea.jpg. Después de cargar el archivo de imagen, actualizaríamos el registro de Chai en la tabla Products para que su columna ImagePath haga referencia a la ruta de acceso de la nueva imagen. Podríamos usar ~/Images/Tea.jpg o simplemente Tea.jpg si decidimos que todas las imágenes de producto se colocarían en la carpeta Images de la aplicación.

Las principales ventajas de almacenar los datos binarios en el sistema de archivos son:

  • Facilidad de implementación como veremos en breve, almacenar y recuperar datos binarios almacenados directamente dentro de la base de datos implica un poco más de código que cuando se trabaja con datos a través del sistema de archivos. Además, para que un usuario vea o descargue datos binarios, debe presentar una dirección URL a esos datos. Si los datos residen en el sistema de archivos del servidor web, la dirección URL es un método directo. Sin embargo, si los datos se almacenan en la base de datos, es necesario crear una página web que recuperará y devolverá los datos de la base de datos.
  • Acceso más amplio a los datos binarios es posible que los datos binarios necesiten ser más accesibles a otros servicios o aplicaciones, los cuales no pueden extraer los datos de la base de datos. Por ejemplo, es posible que las imágenes asociadas a cada producto también necesiten estar disponibles para los usuarios a través de FTP, en cuyo caso deberíamos almacenar los datos binarios en el sistema de archivos.
  • Rendimiento si los datos binarios se almacenan en el sistema de archivos, la demanda y la congestión de red entre el servidor de base de datos y el servidor web serán menores que si los datos binarios se almacenan directamente dentro de la base de datos.

La principal desventaja de almacenar datos binarios en el sistema de archivos es que esto desacopla los datos de la base de datos. Si se elimina un registro de la tabla Products, el archivo asociado en el sistema de archivos del servidor web no se elimina automáticamente. Debemos escribir código adicional para eliminar el archivo o el sistema de archivos quedará desordenado con archivos huérfanos sin usar. Además, al realizar una copia de seguridad de la base de datos, también debemos asegurarnos de realizar copias de seguridad de los datos binarios asociados en el sistema de archivos. Mover la base de datos a otro sitio o servidor plantea desafíos similares.

Como alternativa, los datos binarios se pueden almacenar directamente en una base de datos de Microsoft SQL Server 2005 mediante la creación de una columna de tipo varbinary. Al igual que con otros tipos de datos de longitud variable, puede especificar una longitud máxima de los datos binarios que se pueden mantener en esta columna. Por ejemplo, para reservar como máximo 5000 bytes, use varbinary(5000); varbinary(MAX) permite el tamaño máximo de almacenamiento, aproximadamente 2 GB.

La principal ventaja de almacenar datos binarios directamente en la base de datos es el acoplamiento estricto entre los datos binarios y el registro de la base de datos. Esto simplifica considerablemente las tareas de administración de bases de datos, como las copias de seguridad o el traslado de la base de datos a otro sitio o servidor. Además, la eliminación de un registro elimina automáticamente los datos binarios correspondientes. También hay ventajas más sutiles al almacenar los datos binarios en la base de datos. Consulte Almacenar archivos binarios directamente en la base de datos mediante ASP.NET 2.0 para obtener una explicación más detallada.

Nota:

En Microsoft SQL Server 2000 y versiones anteriores, el tipo de datos varbinary tenía un límite máximo de 8000 bytes. Para almacenar hasta 2 GB de datos binarios, el tipo de datos image debe usarse en su lugar. Sin embargo, con la adición de MAX en SQL Server 2005, el tipo de datos image ha quedado en desuso. Todavía se admite para la compatibilidad con versiones anteriores, pero Microsoft ha anunciado que el tipo de datos image se quitará en una versión futura de SQL Server.

Si trabaja con un modelo de datos anterior, es posible que vea el tipo de datos image. La tabla Categories de la base de datos Northwind tiene una columna Picture que se puede usar para almacenar los datos binarios de un archivo de imagen para la categoría. Dado que la base de datos Northwind tiene sus raíces en Microsoft Access y versiones anteriores de SQL Server, esta columna es de tipo image.

Para este tutorial y los tres siguientes, usaremos ambos enfoques. La tabla Categories ya tiene una columna Picture para almacenar el contenido binario de una imagen para la categoría. Agregaremos una columna adicional, BrochurePath, para almacenar una ruta de acceso a un PDF en el sistema de archivos del servidor web, que se puede usar para proporcionar información general de calidad impresa y pulida de la categoría.

Paso 3: Agregar la columna BrochurePath a la tabla Categories

Actualmente, la tabla Categories tiene solo cuatro columnas: CategoryID, CategoryName, Description y Picture. Además de estos campos, es necesario agregar uno nuevo que apunte al folleto de la categoría (si existe). Para agregar esta columna, vaya al Explorador de servidores, explore en profundidad las tablas, haga clic con el botón derecho en la tabla Categories y elija Abrir definición de tabla (vea la figura 5). Si no ve el Explorador de servidores, seleccione la opción Explorador de servidores en el menú Ver o presione Ctrl+Alt+S.

Agregue una nueva columna varchar(200) a la tabla Categories denominada BrochurePath y permita NULL. Por último, haga clic en el icono Guardar (o presione Ctrl+S).

Add a BrochurePath Column to the Categories Table

Figura 5: Agregar una nueva columna BrochurePath a la tabla Categories (haga clic aquí para ver la imagen en tamaño completo)

Paso 4: Actualizar la arquitectura para usar las columnas Picture yBrochurePath

El elemento CategoriesDataTable de la capa de acceso a datos (DAL) tiene actualmente cuatro DataColumn definidas: CategoryID, CategoryName, Description y NumberOfProducts. Cuando originalmente diseñamos esta DataTable en el tutorial Creación de una capa de acceso a datos, la CategoriesDataTable solo tenía las tres primeras columnas; la columna NumberOfProducts se agregó en el tutorial Maestro y detalles mediante una lista con viñetas de registros maestros con un control DataList de detalles (C#).

Como se describe en Creación de una capa de acceso a datos, DataTables en el conjunto de datos con tipo componen los objetos empresariales. TableAdapters es responsable de comunicarse con la base de datos y rellenar los objetos empresariales con los resultados de la consulta. CategoriesDataTable se rellena mediante CategoriesTableAdapter, que tiene tres métodos de recuperación de datos:

  • GetCategories() ejecuta la consulta principal de TableAdapter y devuelve los campos CategoryID, CategoryName y Description de todos los registros de la tabla Categories. La consulta principal la usan los métodos generados automáticamente Insert y Update.
  • GetCategoryByCategoryID(categoryID) devuelve los campos CategoryID, CategoryName y Description de la categoría cuyo CategoryID es igual a categoryID.
  • GetCategoriesAndNumberOfProducts(): devuelve los campos CategoryID, CategoryName y Description para todos los registros de la tabla Categories. También usa una subconsulta para devolver el número de productos asociados a cada categoría.

Observe que ninguna de estas consultas devuelve la Picture o BrochurePath de la tabla Categories; ni las CategoriesDataTable proporciona DataColumn para estos campos. Para trabajar con las propiedades Picture y BrochurePath, primero es necesario agregarlas a CategoriesDataTable y, después, actualizar la clase CategoriesTableAdapter para devolver estas columnas.

Adición de los elementos Picture yBrochurePath``DataColumn

Empiece agregando estas dos columnas a CategoriesDataTable. Haga clic con el botón derecho en el encabezado de CategoriesDataTable, seleccione Agregar en el menú contextual y, a continuación, elija la opción Columna. Esto creará una nueva DataColumn en la tabla de datos denominada Column1. Cambie el nombre de esta columna a Picture. En la ventana Propiedades, establezca la propiedad DataType de DataColumn como System.Byte[] (esta opción no aparece en la lista desplegable; debe escribirla).

Create a DataColumn Named Picture whose DataType is System.Byte[]

Figura 6: Crear una DataColumn denominada Picture cuyo DataType es System.Byte[] (haga clic para ver la imagen en tamaño completo)

Agregue otra DataColumn a DataTable y denomínela BrochurePath con el valor predeterminado DataType (System.String).

Devolver los valores Picture y BrochurePathde TableAdapter

Con estas dos DataColumn agregadas a CategoriesDataTable, estamos listos para actualizar CategoriesTableAdapter. Podríamos devolver ambos valores de columna en la consulta principal TableAdapter, pero esto devolvería los datos binarios cada vez que se invoca el método GetCategories(). En su lugar, vamos a actualizar la consulta TableAdapter principal para devolver BrochurePath y crear un método de recuperación de datos adicional que devuelva una columna de categoría Picture determinada.

Para actualizar la consulta TableAdapter principal, haga clic con el botón derecho en el encabezado de CategoriesTableAdapter y elija la opción Configurar en el menú contextual. Esto abre el Asistente para la configuración de TableAdapter, que hemos visto en varios tutoriales anteriores. Actualice la consulta para devolver BrochurePath y haga clic en Finalizar.

Update the Column List in the SELECT Statement to Also Return BrochurePath

Figura 7: Actualizar la lista de columnas en la instrucción SELECT para devolver también BrochurePath (haga clic para ver la imagen en tamaño completo)

Cuando se usan instrucciones SQL ad-hoc para TableAdapter, la actualización de la lista de columnas de la consulta principal actualiza la lista de columnas para todos los métodos de consulta SELECT de TableAdapter. Esto significa que el método GetCategoryByCategoryID(categoryID) se ha actualizado para devolver la columna BrochurePath, lo que podría ser lo que pretendemos. Sin embargo, también se ha actualizado la lista de columnas en el método GetCategoriesAndNumberOfProducts(), ¡y se ha quitado la subconsulta que devuelve el número de productos para cada categoría! Por tanto, es necesario actualizar la consulta SELECT de este método. Haga clic con el botón derecho en el método GetCategoriesAndNumberOfProducts(), elija Configurar y vuelva a revierta la consulta SELECT a su valor original:

SELECT CategoryID, CategoryName, Description, 
       (SELECT COUNT(*) 
            FROM Products p 
            WHERE p.CategoryID = c.CategoryID) 
       as NumberOfProducts
FROM Categories c

A continuación, cree un nuevo método TableAdapter que devuelva un valor de columna de categoría Picture determinada. Haga clic con el botón derecho en el encabezado de CategoriesTableAdapter y elija la opción Agregar consulta para iniciar el Asistente para la configuración de consultas de TableAdapter. El primer paso de este asistente nos pregunta si queremos consultar datos mediante una instrucción SQL ad-hoc, un nuevo procedimiento almacenado o uno existente. Seleccione Usar instrucciones SQL y haga clic en Siguiente. Puesto que se va a devolver una fila, elija la opción SELECT, que devuelve filas del segundo paso.

Select the Use SQL statements Option

Figura 8: Seleccionar la opción Usar instrucciones SQL (haga clic para ver la imagen en tamaño completo)

Since the Query Will Return a Record from the Categories Table, Choose SELECT which returns rows

Figura 9: Dado que la consulta devolverá un registro de la tabla Categorías, elija SELECT, que devuelve filas (haga clic para ver la imagen en tamaño completo)

En el tercer paso, escriba la siguiente consulta SQL y haga clic en Siguiente:

SELECT     CategoryID, CategoryName, Description, BrochurePath, Picture
FROM       Categories
WHERE      CategoryID = @CategoryID

El último paso es elegir el nombre del nuevo método. Use FillCategoryWithBinaryDataByCategoryID y GetCategoryWithBinaryDataByCategoryID para los patrones Rellenar un DataTable y Devolver una DataTable, respectivamente. Haga clic en Finalizar para completar el asistente.

Choose the Names for the TableAdapter s Methods

Figura 10: Elegir los nombres de los métodos TableAdapter (haga clic para ver la imagen en tamaño completo)

Nota:

Después de completar el Asistente para la configuración de consultas de TableAdapter, puede ver un cuadro de diálogo que le informa de que el nuevo texto del comando devuelve datos con un esquema diferente del esquema de la consulta principal. En resumen, el asistente observa que la consulta principal GetCategories() de TableAdapter devuelve un esquema diferente al que acabamos de crear. Sin embargo, esto es lo que queremos, así que ignore este mensaje.

Además, tenga en cuenta que, si usa instrucciones SQL ad-hoc y usa el asistente para cambiar la consulta principal de TableAdapter en algún momento posterior, modificará la lista de columnas de la instrucción SELECT del método GetCategoryWithBinaryDataByCategoryID para incluir solo esas columnas de la consulta principal (es decir, quitará la columna Picture de la consulta). Tendrá que actualizar manualmente la lista de columnas para devolver la columna Picture, similar a lo que hicimos con el método GetCategoriesAndNumberOfProducts() anteriormente en este paso.

Después de agregar las dos DataColumn a la CategoriesDataTable y el método GetCategoryWithBinaryDataByCategoryID a CategoriesTableAdapter, estas clases del Diseñador de conjuntos de datos con tipo deben tener un aspecto similar a la captura de pantalla de la Figura 11.

The DataSet Designer Includes the New Columns and Method

Figura 11: El Diseñador de conjuntos de datos incluye las nuevas columnas y el método

Actualizar la capa de lógica empresarial (BLL)

Con la DAL actualizada, todo lo que queda es aumentar la capa de lógica empresarial (BLL) para incluir un método para el nuevo método CategoriesTableAdapter. Agregue el siguiente método a la clase CategoriesBLL:

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Select, false)] 
public Northwind.CategoriesDataTable 
    GetCategoryWithBinaryDataByCategoryID(int categoryID)
{
    return Adapter.GetCategoryWithBinaryDataByCategoryID(categoryID);
}

Paso 5: Cargar un archivo desde el cliente al servidor web

Al recopilar datos binarios, suele ser un usuario final quien proporciona estos datos. Para capturar esta información, el usuario debe poder cargar un archivo desde su equipo al servidor web. A continuación, los datos cargados deben integrarse con el modelo de datos, lo que puede significar guardar el archivo en el sistema de archivos del servidor web y agregar una ruta de acceso al archivo de la base de datos o escribir el contenido binario directamente en la base de datos. En este paso, veremos cómo permitir que un usuario cargue archivos desde su equipo al servidor. En el siguiente tutorial, nos enfocaremos en integrar el archivo cargado con el modelo de datos.

El nuevo control web FileUpload de ASP.NET 2.0 proporciona un mecanismo para que los usuarios envíen un archivo desde su equipo al servidor web. El control FileUpload se representa como un elemento <input> cuyo atributo type está establecido como archivo, el cual los exploradores muestran como un cuadro de texto con un botón Examinar. Al hacer clic en el botón Examinar, se abre un cuadro de diálogo desde el que el usuario puede seleccionar un archivo. Cuando se devuelve el formulario, el contenido del archivo seleccionado se envía junto con el postback. En el lado servidor, se puede acceder a la información sobre el archivo cargado a través de las propiedades del control FileUpload.

Para mostrar la carga de archivos, abra la página FileUpload.aspx de la carpeta BinaryData, arrastre un control FileUpload desde el Cuadro de herramientas al Diseñador y establezca la propiedad ID del control como UploadTest. A continuación, agregue un control Button Web; para ello, establezca sus propiedades ID y Text en UploadButton y Cargar archivo seleccionado, respectivamente. Por último, coloque un control Etiqueta web debajo del botón, desactive su propiedad Text y establezca su propiedad ID como UploadDetails.

Add a FileUpload Control to the ASP.NET Page

Figura 12: Agregar un control FileUpload a la página de ASP.NET (haga clic para ver la imagen a tamaño completo)

En la Figura 13, se muestra esta página vista desde un explorador. Tenga en cuenta que, al hacer clic en el botón Examinar, se abre un cuadro de diálogo de selección de archivos, lo que permite al usuario seleccionar un archivo de su equipo. Una vez seleccionado un archivo, al hacer clic en el botón Cargar archivo seleccionado, se produce un postback que envía el contenido binario del archivo seleccionado al servidor web.

The User Can Select a File to Upload from their Computer to the Server

Figura 13: El usuario puede seleccionar un archivo para cargar desde su equipo al servidor (haga clic para ver la imagen en tamaño completo)

Durante el postback, el archivo cargado se puede guardar en el sistema de archivos o se puede trabajar directamente con sus datos binarios a través de Stream. En este ejemplo, vamos a crear una carpeta ~/Brochures y guardar el archivo cargado allí. Para comenzar, agregue la carpeta Brochures al sitio como una subcarpeta del directorio raíz. A continuación, cree un controlador de eventos para el evento Click de UploadButton y agregue el código siguiente:

protected void UploadButton_Click(object sender, EventArgs e)
{
    if (UploadTest.HasFile == false)
    {
        // No file uploaded!
        UploadDetails.Text = "Please first select a file to upload...";            
    }
    else
    {
        // Display the uploaded file's details
        UploadDetails.Text = string.Format(
                @"Uploaded file: {0}<br />
                  File size (in bytes): {1:N0}<br />
                  Content-type: {2}", 
                  UploadTest.FileName, 
                  UploadTest.FileBytes.Length,
                  UploadTest.PostedFile.ContentType);
        // Save the file
        string filePath = 
            Server.MapPath("~/Brochures/" + UploadTest.FileName);
        UploadTest.SaveAs(filePath);
    }
}

El control FileUpload proporciona una variedad de propiedades para trabajar con los datos cargados. Por ejemplo, la propiedad HasFile indica si el usuario ha cargado un archivo, mientras que la propiedad FileBytes proporciona acceso a los datos binarios cargados como una matriz de bytes. El controlador de eventos Click se inicia asegurándose de que se ha cargado un archivo. Si se ha cargado un archivo, la etiqueta muestra el nombre del archivo cargado, su tamaño en bytes y su tipo de contenido.

Nota:

Para asegurarse de que el usuario carga un archivo, puede comprobar la propiedad HasFile y mostrar una advertencia si es false, o puede usar el control RequiredFieldValidator en su lugar.

SaveAs(filePath) de FileUpload guarda el archivo cargado en la filePath especificada. filePath debe ser una ruta de acceso física (C:\Websites\Brochures\SomeFile.pdf) en lugar de una ruta de accesovirtual (/Brochures/SomeFile.pdf). El método Server.MapPath(virtPath) toma una ruta de acceso virtual y devuelve su ruta de acceso física correspondiente. Aquí, la ruta de acceso virtual es ~/Brochures/fileName, donde fileName es el nombre del archivo cargado. Consulte el Método Server.MapPath para obtener más información sobre las rutas de acceso virtuales y físicas, y el uso de Server.MapPath.

Después de completar el controlador de eventos Click, dedique un momento a probar la página en un explorador. Haga clic en el botón Examinar y seleccione un archivo de la unidad de disco duro y, a continuación, haga clic en el botón Cargar archivo seleccionado. El postback enviará el contenido del archivo seleccionado al servidor web, que mostrará información sobre el archivo antes de guardarlo en la carpeta ~/Brochures. Después de cargar el archivo, vuelva a Visual Studio y haga clic en el botón Actualizar en el Explorador de soluciones. Debería ver el archivo que acaba de cargar en la carpeta ~/Brochures.

The File EvolutionValley.jpg Has Been Uploaded to the Web Server

Figura 14: El archivo EvolutionValley.jpg se ha cargado en el servidor web (haga clic para ver la imagen en tamaño completo)

EvolutionValley.jpg Was Saved to the ~/Brochures Folder

Figura 15: EvolutionValley.jpg se guardó en la carpeta ~/Brochures

Sutilezas al guardar archivos cargados en el sistema de archivos

Hay varias sutilezas que deben solucionarse al guardar archivos de carga en el sistema de archivos del servidor web. En primer lugar, está el problema de la seguridad. Para guardar un archivo en el sistema de archivos, el contexto de seguridad en el que se ejecuta la página ASP.NET debe tener permisos de escritura. El servidor web de desarrollo de ASP.NET se ejecuta en el contexto de la cuenta de usuario actual. Si usa Internet Information Services (IIS) de Microsoft como servidor web, el contexto de seguridad depende de la versión de IIS y su configuración.

Otro desafío al guardar archivos en el sistema de archivos gira en torno a la nomenclatura de los archivos. Actualmente, nuestra página guarda todos los archivos cargados en el directorio ~/Brochures con el mismo nombre del archivo en el equipo del cliente. Si el usuario A carga un folleto con el nombre Brochure.pdf, el archivo se guardará como ~/Brochure/Brochure.pdf. Sin embargo, ¿qué ocurre si algún tiempo más tarde el usuario B carga un archivo de folleto diferente que tiene el mismo nombre de archivo (Brochure.pdf)? Con el código que tenemos ahora, el archivo del usuario A se sobrescribirá con la carga del usuario B.

Hay varias técnicas para resolver conflictos de nombres de archivo. Una opción es prohibir la carga de un archivo si ya existe uno con el mismo nombre. Con este enfoque, cuando el usuario B intente cargar un archivo denominado Brochure.pdf, el sistema no guardaría su archivo y, en su lugar, mostraría un mensaje que informa al usuario B que debe cambiar el nombre del archivo e intentarlo de nuevo. Otro enfoque consiste en guardar el archivo mediante un nombre de archivo único, que podría ser un identificador único global (GUID) o el valor de las columnas de clave principal del registro de base de datos correspondiente (suponiendo que la carga esté asociada a una fila determinada en el modelo de datos). En el siguiente tutorial, exploraremos estas opciones con más detalle.

Desafíos implicados en cantidades muy grandes de datos binarios

En estos tutoriales, se asume que los datos binarios capturados son de un tamaño considerable. Trabajar con grandes cantidades de archivos de datos binarios que pesan varios megabytes o son más grandes presenta nuevos desafíos que están fuera del ámbito de estos tutoriales. Por ejemplo, de forma predeterminada, ASP.NET rechazará las cargas de más de 4 MB, aunque esto se puede configurar a través del elemento <httpRuntime> en Web.config. IIS impone también sus propias limitaciones de tamaño de carga de archivos. Además, el tiempo necesario para cargar archivos grandes puede superar los 110 segundos predeterminados que ASP.NET espera por una solicitud. Asimismo, existen problemas de memoria y rendimiento que surgen al trabajar con archivos grandes.

El control FileUpload no es práctico para cargas de archivos grandes. A medida que el contenido del archivo se publica en el servidor, el usuario final debe esperar pacientemente sin tener confirmación del progreso de su carga. Esto no es tanto un problema al tratar con archivos más pequeños que se pueden cargar en unos segundos, pero puede ser un problema al tratar con archivos más grandes que pueden tardar minutos en cargarse. Existe una variedad de controles de carga de archivos de terceros que son más adecuados para controlar cargas grandes y muchos de estos proveedores proporcionan indicadores de progreso y administradores de carga ActiveX que presentan una experiencia de usuario mucho más pulida.

Si la aplicación necesita controlar archivos grandes, deberá investigar cuidadosamente los desafíos relacionados y encontrar soluciones adecuadas para sus necesidades concretas.

Resumen

La creación de una aplicación que necesita capturar datos binarios presenta una serie de desafíos. En este tutorial, hemos explorado los dos primeros: decidir dónde almacenar los datos binarios y permitir que un usuario cargue contenido binario a través de una página web. En los tres tutoriales siguientes, veremos cómo asociar los datos cargados a un registro de la base de datos, así como cómo mostrar los datos binarios junto con sus campos de datos de texto.

¡Feliz programación!

Lecturas adicionales

Para obtener más información sobre los temas tratados en este tutorial, consulte los siguientes recursos:

Acerca del autor

Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha trabajado con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Puede ponerse en contacto con él a través de mitchell@4GuysFromRolla.com. o de su blog, que se puede encontrar en http://ScottOnWriting.NET.

Agradecimientos especiales a

Esta serie de tutoriales contó con la revisión de muchos revisores que fueron de gran ayuda. Los revisores principales de este tutorial fueron Teresa Murphy y Bernadette Leigh. ¿Le interesaría revisar mis próximos artículos de MSDN? Si fuera así, escríbame a mitchell@4GuysFromRolla.com.