0:00 0 0
ASP NET descargar archivo desde el servidor

ASP NET descargar archivo desde el servidor

  DrUalcman |  abril 192020

¿Alguna vez has necesitado que cuando se haga click en un enlace se descargue directamente el archivo? Seguramente la respuesta es SI. Para ello tenemos directamente en el TAG HTML posibilidad de indicar al navegador que realice esta acción, simplemente agregando la palabra download en el tag A. Pero he encontrado un par de "problemillas" con respecto de este atributo:

  1. No es compatible con todos los exploradores
  2. Dependiendo del explorador necesita diferente nomenglatura, aunque si piensas en los más complicados es válido para los otros. a usar download="nombre" o sólo download. Más información en los enlaces adjuntos.
  3. Si el archivo en cuestión no está en el mismo dominio, pero sí en el mismo servidor.
  4. Si el archivo en cuestión no está en tu servidor.

Si estamos utilizando la nomglatura con el TAG A download="nombre" y el archino no se encuentra dentro de mi misa URL, no va a comenzar la transferencia directamente y abrirá el archivo como un enlace normal y corriente. Esto supone varios problemas, el primero que el usuario puede, directamente enlazar el archivo y es posible que esto no nos interesa, sobre todo incluso si es desde nuestro servidor, ya que ocasionaría que pudieran sobrecargar el servidor.

Gracias a los eventos postback de webforms si utilizamos un button, o incluso in linckbutton el usuario no puede ver el enlace y, tras la acción de postback podemos realizar el envío del archivo con un poco de código.

Otra ventaja que le veo a este truco de hoy, es que podemos obtener los archivos desde cualquier ruta de nuestro disco curo de forma segura, ya que el usuario no tiene acceso directo a dicha carpeta. No he comprobado el tema de permisos, pero en la carpeta de donde estoy descargando los archivos el usuario de IIS tiene acceso de lectura, sería lo más aconsesable para así no tener problemas de permisos de usuario.

El código mágico

Obviamente necesitamos un objeto ASP que tenga un evento OnClick para ejecutar la función desde código. Aqui el ejemploÑ

    protected void btnDownload_Click(object sender, EventArgs e)
{
string FileName = ((LinkButton)sender).CommandArgument;

HttpResponse response = HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "application/octet-stream";
response.AddHeader("Content-Disposition", "attachment; filename=" + FileName + ";");
drualcman.archivos a = new drualcman.archivos();
Stream file = a.GetStreamFile("carpeta" + FileName);
response.BinaryWrite(a.ReadToEnd(file));
a = null;
response.Flush();
response.End();
}

En mi ejemplo estoy utilizando una tabla, por lo que le paso el nombre del archivo como  argumento, pero si siempre se desea enviar un mismo archivo sólo teneis que cambiar esa línea.

Con éste código se envía el archivo en bruto en una nueva respuesta desde el servidor. El tipo de contenido, como en mi caso son imágenes o vídeos, lo he puesto como octet-stream, pero funciona aunque lo tengas con cualquier otro, ya que una vez me equivoque y lo mande como image.jpeg y como text-plain y de todos modos me descargaba correctamente el archivo.

Funciones extra necesarias

Para que no te vuelvas loco, habrás visto que en el código llamo a otra función GetStreamFile para obtener el archivo y ReadToEnd para convertirlo en bytes. Pues nada, aqui teneis las otras dos funciones:

        /// 
/// Adjuntar archivo al un mail
///

/// ruta del archivo
///
public Stream GetStreamFile(string filePath)
{
using (FileStream fileStream = File.OpenRead(filePath))
{
MemoryStream memStream = new MemoryStream();
memStream.SetLength(fileStream.Length);
fileStream.Read(memStream.GetBuffer(), 0, (int)fileStream.Length);

return memStream;
}
}

///
/// Convertir una variable Stream en una variable byte[]
///

///
///
public byte[] ReadToEnd(Stream stream)
{
long originalPosition = 0;

if (stream.CanSeek)
{
originalPosition = stream.Position;
stream.Position = 0;
}

try
{
byte[] readBuffer = new byte[4096];

int totalBytesRead = 0;
int bytesRead;

while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
{
totalBytesRead += bytesRead;

if (totalBytesRead == readBuffer.Length)
{
int nextByte = stream.ReadByte();
if (nextByte != -1)
{
byte[] temp = new byte[readBuffer.Length * 2];
Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
readBuffer = temp;
totalBytesRead++;
}
}
}

byte[] buffer = readBuffer;
if (readBuffer.Length != totalBytesRead)
{
buffer = new byte[totalBytesRead];
Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
}
return buffer;
}
finally
{
if (stream.CanSeek)
{
stream.Position = originalPosition;
}
}
}

Y eso es todo amigos, espero que os guste este truco.

0 Comentarios

 
 
 

Archivo