En esta ocasión vamos a realizar el proceso de:
1.- Guardar, en una carpeta del servidor, archivos, los cuales deben guardarse con otros nombres (nombres autogenerados, por ejemplo: 3F2504E0-4F89-11D3-9A0C-0305E82C3301).
2.- Guardar en la base de datos, el nombre original del archivo y el nuevo nombre.
3.- Recuperar los nombres y rutas de los archivos subidos al servidor y listarlos en un gridview.
4.- Basado en los puntos 2 y 3, permitir descargar los archivos con su original nombre.
Para completar este ciclo voy a crear en la base de datos una tabla llamada document_attachment y los respectivos procedimientos almacenados para la gestión de archivos.
Entonces, este es el script T-SQL que uso para el caso ( una tabla y 3 procedimientos almacenados):
CREATE TABLE [dbo].[document_attachment]( [attachmentid] [int] IDENTITY(1,1) NOT NULL, [filename] [varchar](250), [fileguid] [varchar](50), CONSTRAINT [pk_document_attachment] PRIMARY KEY CLUSTERED ([attachmentid] ASC) )GO
--------------------------------------------------
CREATE PROCEDURE[dbo].[document_attachment_GetAll]
AS
SELECT
[attachmentid],
[filename],
[fileguid]
FROM [dbo].[document_attachment]
GO
--------------------------------------------------
CREATE PROCEDURE[dbo].[document_attachment_Insert]
@attachmentid int output,
@filename varchar(250),
@fileguid varchar(50)
AS
INSERT INTO[dbo].[document_attachment](
[filename],
[fileguid]
)
VALUES(
@filename,
@fileguid
)
SET @attachmentid = SCOPE_IDENTITY()
GO
--------------------------------------------------
CREATE PROCEDURE[dbo].[document_attachment_GetByGuid]
@guid varchar(50)
AS
SELECT
[filename],
[fileguid]
FROM [dbo].[document_attachment]
WHERE [fileguid] = @guid
--------------------------------------------------
Antes de pasar al código ASP.NET, les recomiendo que guarden la cadena de conexión a la base de datos en el web config (para el ejemplo está sin encriptar, …pero uds deben encriptarlo)
<connectionStrings>
<add name="DB" connectionString="server=.\SQLEXPRESS;database=my_database;uid=sa;pwd=xxxxxxxxxxxxxxx" />
</connectionStrings>
Otra cosa, deben crear una carpeta llamada Uploads (en la raiz de su aplicación), que es donde se guardarán los archivos que se suben… a esta carpeta hay que darle permisos de lectura/escritura al usuario SERVICIO DE RED.
Ahora sí, necesitamos dos páginas aspx:
1.- Uploader.aspx, que es la página que permite subir los archivos al servidor e, inmediatamente después actualiza el contenido del gridview con los archivos subidos.
2.- Downloader.aspx, que es la página intermedia para descargar archivos… gracias a esta página podemos evitar que el usuario sepa desde qué carpeta se descargan los archivos y además en el código de esta página pueden hacer las verificaciones que quieran antes de permitirle al usuario descargar el archivo. Tú me preguntarás: puedo usar un archivo ashx (que es más ligero) en lugar de una pagina aspx para descargar? la respuesta es sí.
Este es el código completo:
Uploader.aspx
<!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>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div style="width: 350px; background-color: #f2ceea; padding: 10px; margin: 20px"> Seleccione archivo:<asp:FileUpload ID="FileUpload1" runat="server" /> <hr /> <asp:Button ID="btnUpload" runat="server" Text="Guardar archivo" /> </div> <asp:GridView ID="griddocument_attachment" BackColor="lightgreen" Width="400px" runat="server" AutoGenerateColumns="False" DataKeyNames="fileguid" HeaderStyle-HorizontalAlign="Left" HeaderStyle-VerticalAlign="Top" RowStyle-VerticalAlign="Top"> <Columns> <asp:TemplateField HeaderStyle-Width="3%"> <ItemTemplate> <asp:Image ID="Image1" ImageUrl="~/images/file.png" runat="server" /> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Archivo"> <ItemTemplate> <%# Eval("filename")%> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Descargar" HeaderStyle-Width="5%"> <ItemTemplate> <asp:HyperLink ID="hplAttachment" Text='<%# Eval("filename")%>' NavigateUrl='<%# String.Format("~/Downloader.aspx?guid={0}", Eval("fileguid")) %>' ImageUrl="~/images/download.png" runat="server"></asp:HyperLink> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> </form> </body> </html>
En el gridview, el hyperlink con la imagen download.png es quien tiene el enlace de descarga, parecido a esto:
http://localhost/Test_UI/Downloader.aspx?guid=ee4a00fa-cfa9-490e-a65c-12d652fd2c00
código VB.NET:
Imports System.Data
Imports System.Data.SqlClient
Partial Public Class Uploader Inherits System.Web.UI.Page Private GlobalDBConnection As String = ConfigurationManager.ConnectionStrings("DB").ConnectionString Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If IsPostBack Then Return document_attachment_GetAll() End Sub Protected Sub btnUpload_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnUpload.Click If Not FileUpload1.HasFile Then Return Dim fileguid As String = Guid.NewGuid.ToString Dim FileName As String = FileUpload1.FileName Try FileUpload1.SaveAs(Server.MapPath("./Uploads/") & fileguid) Catch 'error.... Return End Try Dim i As Integer = document_attachment_Insert(FileName, fileguid) If i = 0 Then 'error.... Else document_attachment_GetAll() End If End Sub Sub document_attachment_GetAll() Dim connection As New SqlConnection(GlobalDBConnection) Dim command As New SqlDataAdapter("document_attachment_GetAll", connection) command.SelectCommand.CommandType = CommandType.StoredProcedure Dim dt As New DataTable() command.Fill(dt) griddocument_attachment.DataSource = dt griddocument_attachment.DataBind() End Sub Function document_attachment_Insert(ByVal filename As String, ByVal fileguid As String) As Integer Dim connection As New SqlConnection(GlobalDBConnection) Dim command As New SqlCommand("document_attachment_Insert", connection) command.CommandType = CommandType.StoredProcedure Dim p_attachmentid As New SqlParameter("@attachmentid", SqlDbType.Int) p_attachmentid.Direction = ParameterDirection.Output command.Parameters.Add(p_attachmentid) command.Parameters.AddWithValue("@filename", filename) command.Parameters.AddWithValue("@fileguid", fileguid) command.Connection.Open() Try command.ExecuteNonQuery().ToString() Catch Return 0 Finally command.Connection.Close() End Try If Not IsDBNull(p_attachmentid.Value) Then Return CInt(p_attachmentid.Value) Else Return 0 End If End Function End Class
Downloader.aspx
en el lado aspx tiene absolutamente nada, excepto el tag Page:
<%@ Page Language="vb" …………………………………………………… %>
código VB.NET:
Imports System.Data
ImportsSystem.Data.SqlClient
Partial Public Class Downloader
Inherits System.Web.UI.Page
Private GlobalDBConnection As String= ConfigurationManager.ConnectionStrings("DB").ConnectionString
Protected SubPage_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim guid As String= Request.QueryString("guid")
If String.IsNullOrEmpty(guid) Then Return
' aquí puedes poner las validaciones que quieras
'............ ....................
Dim dt As DataTable = document_attachment_GetByGuid(guid)
If dt.Rows.Count = 0 Then Return
Dim OriginalFileName As String= dt.Rows(0)("filename")
DownloadFile(Server.MapPath("./Uploads/") & guid, OriginalFileName)
End Sub
Public Sub DownloadFile(ByVal FilePath As String, ByVal OriginalFileName As String)
Dim fs As IO.FileStream = Nothing
'obtenemos el archivo del servidor
fs = IO.File.Open(FilePath, IO.FileMode.Open, IO.FileAccess.Read)
Dim byteBuffer(CInt(fs.Length - 1)) As Byte
fs.Read(byteBuffer, 0, CInt(fs.Length))
fs.Close()
Using ms As New IO.MemoryStream(byteBuffer)
'descargar con su nombre original
Response.AddHeader("Content-Disposition", "attachment; filename=" & OriginalFileName)
ms.WriteTo(Response.OutputStream)
End Using
End Sub
Function document_attachment_GetByGuid(ByVal guid As String) As DataTable
Dim connection As New SqlConnection(GlobalDBConnection)
Dim command As New SqlDataAdapter("document_attachment_GetByGuid", connection)
command.SelectCommand.CommandType = CommandType.StoredProcedure
command.SelectCommand.Parameters.AddWithValue("@guid", guid)
Dim dt As New DataTable()
command.Fill(dt)
Return dt
End Function
End Class
ACTUALIZACIÓN:
Debido a las preguntas que constantemente estoy recibiendo acerca de este post, veo que no ha quedado muy claro... entonces, estoy añadiendo el proyecto completo para que lo descarguen y lo analicen con más calma.
Debido a que estoy incluyendo la base de datos en la carpeta app_data, lo único que tendrían que modificar en el we.config antes de correr el proyecto es el nombre del servidor de su bd... osea reemplacen .\SS2008 por el nombre del servidor que Uds le hayan puesto.
Es todo, espero que les sea de utilidad.
;)
;)
50 comentarios:
Hola Segundo, muy interesante el tema de Subir y descargar archivos del servidor mediante pagina aspx intermedia, de hecho me motivo a probarlo, pero al copiar el codigo y luego al compilarlo en visual studio me aparecieron unos errores, a que se debera??
espero me puedas ayudar.
Hola albolalo,
quiero aclarar que esta línea:
<%@ Page Language="vb" …………………………………………………… %>
no la debes copiar... sino que cada página ya genera su propio tag Page, que conserve el original nomás...
el resto de líneas sí lo puedes copiar y pegar tal cual.
pero dime cuál es el mensaje de error que está apareciendo?
hola.
este error me da:
Could not find stored procedure 'document_attachment_GetAll'.
en la linea donde esta el command.Fill(dt)
creo que es un problema de conexion con la base de dato
ya veo,
no es problema de conexión con la base de datos, se conecta correctamente pero no encuentra el procedimiento almacenado.
entonces no has creado el procedimiento almacenado aún...
corre este t-sql en tu base de datos:
---------------------------------------------------
CREATE PROCEDURE[dbo].[document_attachment_GetAll]
AS
SELECT
[attachmentid],
[filename],
[fileguid]
FROM [dbo].[document_attachment]
GO
---------------------------------------------------
si salen otros errores con respecto a tu base de datos, entonces verifica que has creado correctamente la tabla y los 3 procedimientos almacenados... arriba está el script completo.
Saludos
logre solucionar el problema, habia problema con la conexion, asi que funciono de maravilla.
ahora voy a modificarlo un poco para ocuparlo en un sistema de manejo documentos que estoy creando, voy a probar como funciona para ordenar los documentos en distintas carpetas, o quisas agregar un campo mas a la tabla para poder diferenciar los tipos de documentos.
depues te comento como me fue, muchas gracias por la ayuda.
saludos
una consulta, los archivos son almacenados Uploads, pero cual es el formato de estos archivos? porque no estan en el formato en el que los subi.
ahora, crees que sera posible agregar alguna opcion en la cual se puedan editar los archivos y tambien eliminarlos???
Saludos
pues el formato es el mismo tal cual los subiste, la única diferencia es que se les ha ocultado la extensión...
esto es bueno porque hace que los archivos sólo puedan ser accesados desde la página downloader.aspx, y no repesenta ningún problema porque al momento de ser descargados, reciben su verdadero nombre y extensión.
Con respecto a la opción de editar y eliminar... ok, lo voy a poner, pero será dentro de unos días, por ahora estoy full.
gracias,
tengo una duda, hay alguna diferencia en hacer las consultas de la BD en un procedure que hacerlas desde las paginas aspx. uploader o dowloader.
queria saber si hay alguna posibilidad de plantearte otras consultas sobre un sistema que estoy realizando, en donde voy a utilizar este metodo de almacenamiento de archivo, y de ser asi donde te lo planteo, en este mismo blog o en algun correo?,
gracias de antemano.
saludos
Hola albolalo,
hay muchas ventajas si usas procedimientos almacenados... te paso un link http://www.forosdelweb.com/f21/ventajas-desventajas-procedimientos-almacenados-web-422991/
Sobre tus preguntas, si ellas aportan a que todos aprendamos... mándalas al blog nomás, si es muy confidencial, mándala al correo.
Saludos,
Segundo, te cuento lo que debo hacer...
estoy haciendo un sistema en asp.net en la cual debo almacenar documentos relacionados a informes o analisis de una empresa, es por esto que me intereso este codigo relacionado a subir y descargar archivos del servidor.
el problema es que debo descargar o subir los archivos segun el area de la empresa en que este.
la empresa consta de plantas las cuales contienen areas y estas a la vez se dividen en secciones, en cada seccion se deben almacenar y descargar documentos.
como hago para saber que cada documento que subo o descargo pertenece a alguna seccion de algun area de una cierta planta.
al igual q la tabla "document_attachment" tengo otras para almacenas las plantas, las areas y secciones, asi como tambien al usuario que sube o descarga un docuemnto.
espero que me puedas ayudar.
saludos cordiales.
Albolalo,
he añadido una imagen con una explicación, con respecto a lo que me preguntas, al final de este post.
Saludos,
claro, asi es como tengo la base de dato, mi consulta iba mas relacionada a lo que es codigo en asp,net
Hola segundo, te cuento que agregue un textbox a continuacion del boton "examinar" el cual guarda una Observacion del archivo ingresado en la base de datos en la tabla "document_attachment", luego agregue una columna en el Gridview para mostrar la Observacion de cada archivo que fueron guardadas en la BD.
tambien coloque un boton "editar" en cada fila para poder modificar la columna "Observacion", la idea es modificar el contenido de la BD del campo "observacion" desde la Gridview a traves de un Textbox, pero esto lamentablemente no me ha funcionado, es por esto que pido tu ayuda para poder solucionar este problema, vi uno de tus temas relacionado con esto pero no me funciono.
de antemano muchas gracias
segundo resulta que no me funciona tu codigo, resulta que me sale una entana que dice:
Sys.Webforms.pagerequestmanagerparsererrorexception: the message received from the server could not be parsed. Common
causes for this error are when response is modified by calls to response. write(), response filters, HttpModules, or
server trace is enabled
details: Error parsing near 'HTTP://LOCALHOST/MIN'
estoy haciendo unas pruebas localmente pero no funciona, ayudame es urgente
Hola,
cuál es la acción que realizas en ese preciso momento? estás usando ASP.NET AJAX?
resulta que coloque el codigo tuyo en una opcion que existe en un gridview cuando preciono esta opcion aparese el error pero en codigo no se genera un error pero este aparece de todos modos (no se si me hago entender bien) estoy trabajando en una pagina aspx y tengo instalado el ajax (no recuerdo la version pero recuerdo que le decian atlas creo que es la 2 o la 3) si es a lo que te refieres
PD: cuando dige que estaba haciendo unas pruebas localmente me refiero a que estoy ejecutando esta pagina desde el servidor
joc,
este error realmente no tiene que ver con el código que hice... mas bien es un caso ya conocido y se presenta por conflictos con asp.net ajax u otros casos...
te diría que pruebes mi código en un proyecto aparte (sin ajax) para que descartes el error por ese lado
Por otro lado, te mando un enlace: http://tinyurl.com/25blzu3, dale una chequeada a ver si estás en uno de los casos que mencionan y si encuentras la solución... en caso contrario me avisas para ayudarte.
espero tus comentarios.
segundo, ya me lo ley y si es el caso porque yo uso un updatepanel de modo que solo se crea un postback parcial de la pagina, en la pagina que me diste hay un link aun blog en el cual esta el link de la solucion el cual es un parche o algo asi por lo que entendi (no se mucho ingles que digamos) el caso es que al final dan un codigo en c# pero no se en que momento se debe usar ese codigo o como lo uso, el link es este: http://siderite.blogspot.com/2008/05/how-to-patch-aspnet-ajax-in-order-to.html
agradesco tu ayuda y pronta respuesta
joc,
ese código lo pones en tu página y luego llaams el procedimiento desde el page_load...
pero hagamos algo: mándame el código aspx y vb.net de tu página a mi correo sserrano[@]jabs[.]com[.]pe para revisarlo y poder ayudarte.
Saludos.
Hola Segundo, disculpa que te moleste , pero solo queria saber si hay posibilidad de que me puedas ayudar con los problemas que te plantie.
gracias.
perdona albolalo que me meta pero me parese interesante lo que hiciste, no entiendo bien que es lo que no te funciona, cual es el error que aparese o que pasa
Hola albolalo,
te envié un correo solicitándote el script de tu tabla para mandarte un ejemplo basado en eso... es posible?
si no es posible me avisas para hacerte un ejemplo que más o menos creo que es lo que quieres.
Saludos,
segundo, te acabo de mandar un correo con el script de la base de datos, muchas gracias
BUENOS DIAS, TENGO UNA DUDA SOBRE LA FUNCIONALIDAD DE LA APLICACION, SI ME GUARDA EL ARCHIVO EN LA CARPETA UPLOAD, PERO NO ME MUESTRA EL GRIDVIEW, Y REBICE EN EL SQL Y NO INSERTA LA RUTA.
Hola Marcos,
No guarda la ruta en la tabla de la base de datos para que puedas cambiar de carpeta a tu antojo...
si te fijas en el código de la página Downloader.aspx.vb, allí si incluyo la ruta de los archivos, justo aquí:
Dim OriginalFileName As String= dt.Rows(0)("filename")
DownloadFile(Server.MapPath("./Uploads/") & guid, OriginalFileName)
End Sub
Saludos.
se podra usando lo blob o varbinary en sql server
Hola Ivan,
claro que se puede....
lo único que tendrías que modificar un poquito, en lugar de recuperar el archivo de la ruta física, tendrías que recuperarlo de la bd... algo así:
Dim byteBuffer() As Byte = dt.rows(0)("mi_archivo_en_bytes_desde_la_bd")
Using ms As New IO.MemoryStream(byteBuffer)
Response.AddHeader("Content-Disposition", "attachment; filename=" & OriginalFileName)
ms.WriteTo(Response.OutputStream)
End Using
Saludos.
Hola me gusto mucho la información, pero quiero saber si de pronto tienes el código para C
Hola Andrea,
no tengo el código en csharp, y por ahora no tengo mucho tiempo como para convertirlo a csharp, sorry.
pero te paso un convertidor de codigo VB.NET A c sharp
http://www.developerfusion.com/tools/convert/vb-to-csharp/
Saludos
Hola muy buen aporte, pero me gustaria saber lo siguiente es de un aplicativo que estoy haciendo: Si yo tengo un formulario en donde tiene codigo generado, nombre, cargar documento, fecha de inicio y fecha limite. Como podria aplicar el guardar archivos en una carpeta o una base de datos utilizando la implementacion de N capas teniendo dichos objetos. Por que la mayoria solo adjunta y guardar el archivo, pero no lo implenentan para mas datos como se hace ello podria poner en su blogger un aplicativo que indico o en donde puedo ubicar mas informacion de asp.net. Gracias
Hola Kely,
Lamento no haberte respondido en su momento (he estado muy ocupado)... a ver si entendí: necesitas guardar archivos con más datos (como fecha de inicio, fin).
Lo que puedes hacer es añadirle esos campos a tu tabla, de modo que quedaría sí:
+NombreRealDelArchivo
+NombreGuid
+Descripcion
+FechaInicio
+Fecha Fin
+...
y simplemente se guarda todo al hacer el insert de dicha información en la bd.
Era eso lo que necesitabas?
Saludos
Hola Segundo,
Una consulta, como puedo hacer que se muestre una div que bloquee la pantalla mientras se envia la petición de descargar el archivo.?
Ya logre mostrar el div pero no logro ocultarlo ya que al modificar el header el navegador entiende que no debe actualizar la pagina sino que debe mostrar el modal para guardar el archivo.
Yo deseo ocultar el div justo antes de mostrarse el modal.
Saludos,
Saludos, Tengo un problemas si estoy en internet explore fabuloso, pero si me paso a FireFox desastre. El archivo se descarga como tipo archivo.
Saludos.....y gracias por tu apoyo.
hola!,recien intente hace tu ejemplo con el control fileupload, resulta q visual no reconoce el control fileupload ni el grid,no se si lo hice correctamente, el codigo en el archivo downloader.aspx solo contiene una linea de codigo, el codigo en vb lo copie talcual en mi archivo downloader.aspx.vb, mi duda es sobre el codigo q va en el archivo uploader.aspx y en el archivo uploader.aspx.vb, ya que creo q ´por ahi esta mi error, en el site.master, hay q modificar algo????? espero me puedas ayudar gracias
hola maricela,
si pudieras subir tu proyecto a algún lugar y luego enviarme la url para descargarlo y darle una chequeadita, puedes enviarlo a este correo
sserrano[@]neuronasoft[.]net
remueve los corchetes.
creo que sería la forma más rápida de ayudarte.
Saludos
hola, ya te envie el proyecto en visual 2010, espero me puedas ayudar, gracias, o si es q tu lo tienes y me lo pueden enviar, me ayudarias bastante
maricela,
creo que hay un error... sólo has enviado el archivo .sln
tienes que enviar todo tu proyecto
Saludos
hola, te envie el zip desde hotmal, por q no me dejo desde gmail, espero ahora si lo haya hecho correctamente, gracias
hola maricela,
no ha llegado ningún correo, porfa intenta enviarlo a este correo sserrano[@]jabs[.]com[.]pe
puedes descargarlo de aqui porfa
https://www.dropbox.com/home?email_just_verified=1#!/home
maricela,
el enlace que me has enviado sólo me lleva al home...
tienes que enviarme el enlace correcto.
Saludos
https://www.dropbox.com/s/5s1afbges12j3jw/fileupload_c.zip
ups disculpa, que pena.... perdona, haber si ahora si
Hola a todos,
he incluído un zip con el proyecto completo al final del artículo, para que lo descarguen y hagan la pruebas correspondientes.
Debido a que estoy incluyendo la base de datos en la carpeta app_data, lo único que tendrían que modificar antes de correr el proyecto es el nombre del servidor de su bd... osea reemplacen .\SS2008 por el nombre del servidor que Uds le hayan puesto.
Saludos
hola, es muy buena la informacion y no se si me puedas ayudar con una cosa.
estoy buscando la manera de descarga un archivo que esta un un ftp, ya tengo el codigo y lo descarga pero la cuestion es que yo en el codigo le tengo que decir en que ubicacion lo descarga y lo que quiero es utilizar de la misma manera que estas colocando para descargar los archivos en esta pagina, osea que se tenga la posibilidad de elegir donde se quiere descargar.
Cordialmente
Francisco Prada
Muy buen aporte amigo mis respetos... :) gracias (Y)
Hola Segundo,
Muchas gracias por tu artículo, es muy útil. Lo he probado y me funciona correctamente.
Pero recientemente me está fallando, me hace todo el proceso pero los archivos que me descarga están corruptos, no puedo abrirlos. ¿Sabes qué puede ser?
Muchas gracias.
Un saludo.
Hola buenas tardes, muchisímas gracias por la ayuda adicional, funciona full...
Desde hace unos meses use tu ejemplo para un sistema, ahora estoy tratando de comprimir los archivos y descargar el zip, pero como el nombre se cambia no se como hacerlo, agradeceria tu ayuda, gracias
ALBOLALO me urge contactarme contigo, tengo que hacer algo parecido a lo que mencionas de las carpetas y subir archivos, podrias pasarme tu codigo ??
Estimados amigos, antes agradecerte por el ejemplo, muy bueno, muchas felicidades por el post, comentarte que he realizado todo en C#.NET y me corre todo, pero tengo una duda: cuando descargo los archivos los hace en con el nombre System.Data.DataRow, me pueden ayudar como cambiar esta linea de codigo, ya que a mi parecer es aki donde estoy fallado:
VB: Dim OriginalFileName As String= dt.Rows(0)("filename")
lo que yo hise,
C#: string OriginalFileName = dt.Rows[0].ToString();
Desde ya les agradezco por sus minutos en leer este mensaje.
Saludos desde bolivia.
Publicar un comentario