lunes, 14 de junio de 2010

ASP.NET - Pagina intermedia para descargar archivos

Bien, vamos a suponer que ya hemos subido varios archivos al servidor y que están guardados por separado en varias carpetas.
Ahora, resulta fácil poner un link al archivo de manera que puedan descargarlo, correcto? por ejemplo, digamos que tenemos una carpeta llamada Uploads dentro de la carpeta donde se aloja nuestra aplicación web… y dentro de la carpeta Uploads hay subcarpetas con archivos que hemos ido  subiendo… por ejemplo:
Uploads\Documents\Excel1.xls
Uploads\Documents\Excel2.xls
Uploads\KB\Excel3.xls
Uploads\KB\Excel4.xls

La manera más fácil de poner a disposición estos archivos, es poner links como éstos:
http://www.midominio.com/uploads/documents/excel1.xls
http://www.midominio.com/uploads/KB/excel3.xls

Pero, estoy creando agujeros de seguridad, sin querer estoy diciéndole a todos mis usuarios (en voz alta): POR SI ACASO, TODOS LOS ARCHIVOS IMPORTANTES Y CONFIDENCIALES QUE NADIE DEBE REVISAR ESTÁN EN LA CARPETA UPLOADS/DOCUMENTS AH?, REPITOOOOOOOOOO EN  LA CARPETA UPLOADS/DOCUMENTS… ASÍ QUE YA SABEN, NO TOQUEN NADA.
Obviamente, nadie te va a hacer caso y al poco tiempo ya tendrías que estar buscando otro trabajo ;)
Pero como aquí queremos que conserves tu trabajo, te vamos a enseñar cómo deberías construir los enlaces para distribuir archivos a descargar, sin delatar la verdadera ruta del archivo.

Otra práctica que recomiendo es guardar el archivo con otro nombre y sin extensión, sólo con un codigo generado… esto me permite guardar subir varios archivos con el mismo nombre sin tener que preocuparme por ello… dado que el verdadero nombre se guarda en una tabla y se le restituye ese nombre al momento de descargar el usuario.

Nota: vamos a asumir que uds, queridos lectores, ya guardan los archivos con otro nombre y que para ello usan una tabla como ésta en la bd:
filetable
si no les parece claro, me lo indican en un comentario y a ver si hacemos un post al respecto.
Ahora sí, empecemos con los pasos para descargar archivos:
Paso 1.- Crear una clase llamada Upload.vb, y dentro de ella una enumeración con los distintos módulos que manejan descarga de archivos en mi sistema:
Public ClassUpload

Public EnumType
Document = 1
KB = 2
End Enum

End Class

Paso 2.- Cada vez que construya una url para descargar archivos, debe seguir este formato:
http://www.midominio.com/downloader.aspx?type=1&guid=927826ff-2a59-48ac-a38d-272e64f78658

donde el parámetro type representa el tipo de archivo, en este caso 1, osea que vamos a descargar un archivo de tipo Documento.

Luego, el parámetro guid es el identificador del archivo. Asimismo, este identificador me permitirá recuperar el nombre del archivo.

Paso 3.- Añadir una pagina aspx adicional llamada Downloader.aspx, que contenga el siguiente código:




Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim type As String = Request.QueryString("type")
If String.IsNullOrEmpty(type) Then Return
If Not
IsNumeric(type) Then Return

Dim
guid As String = Request.QueryString("guid")
If String.IsNullOrEmpty(guid) Then Return

Dim
Folder As String = String.Empty

Dim dt As New DataTable


Select Case type
Case Upload.Type.Document
'Recupero de la bd, basado en el parámetro guid
dt = document_attachment.GetByGuid(guid)
'indico cómo se llama el folder donde se encuentra el archivo
Folder = "Document"


Case Upload.Type.KB
'Recupero de la bd, basado en el parámetro guid
dt = KB_attachment.GetByGuid(guid)
'indico cómo se llama el folder donde se encuentra el archivo
Folder = "KB"
End Select


If dt.Rows.Count = 0 Then Return
'este es el nombre guid
Dim OldFileName As String = dt.Rows(0)("fileguid")
'éste es su original nombre
Dim OriginalFileName As String = dt.Rows(0)("filename")
'descargo el archivo
DownloadFile(Server.MapPath("~") & "\Uploads\" & Folder & "\" & OldFileName, 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

Es todo, espero que les sea de utilidad ;)

6 comentarios:

Unknown dijo...

si no les parece claro, me lo indican en un comentario y a ver si hacemos un post al respecto.
SI POR FAVOR

Segundo Serrano dijo...

Hola Isidro,

Con mucho gusto esta semana lo hago.

Saludos,

Segundo Serrano dijo...

Hola Isidro,

Ya está el post
http://dotnet-peru.blogspot.com/2010/06/aspnet-subir-y-descargar-archivos-del.html

cualquier duda, comentas?

Unknown dijo...

Gracias por el post,se que nos sera de mucha utlidad.

FreakyGollum dijo...

hola, me parece genial tu post. Es justo lo que estaba necesitando. Lo que te voy a pedir es otro link para ver el formato cuando se construye una url ya que no me permite verlo, sino que me direcciona a una pagina de hosting. Tambien si podrias explicar un poco mas lo de la tabla en la base de datos para guardar los nombres de los archivos.
Gracias!

Segundo Serrano dijo...

Hola,

no sé si leíste este post.
http://dotnet-peru.blogspot.com/2010/06/aspnet-subir-y-descargar-archivos-del.html

Saludos