Inicio / Desarrollo web / Forzar descargas de archivos con PHP

Forzar descargas de archivos con PHP

En algunas ocasiones necesitamos que un archivo sea para descargar y no para ser visualizado en el navegador. Esto se puede hacer utilizando una tecnología del lado del servidor, como puede ser PHP.

Veremos dos formas de hacerlo, una básica y otra más avanzada pero también más segura.

Forzar descarga (Básico)

Primero creamos un archivo llamado, por ejemplo, “download.php“, en el que tendríamos que colocar el siguiente código:

<?php
$file = $_GET['file'];
header("Content-disposition: attachment; filename=$file");
header("Content-type: application/octet-stream");
readfile($file);
?>

Con “Content-disposition: attachment; filename=$file” indicamos que el archivo se debe mostrar como adjunto (para descargar).

Y para crear el enlace pondríamos:

<a href="download.php?file=imagen.jpg">Descargar</a>

Forzar descarga (Seguro)

Ahora veamos cómo hacer el script más seguro.

Por un lado, el script anterior permite descargar cualquier archivo del servidor. Por ejemplo, cualquiera podría descargar nuestro archivo ‘index.php‘ con solo cambiar la ruta de descarga por “download.php?file=../index.php“.

Además, vamos a comprobar previamente que el archivo existe, para evitar errores en caso de que el archivo no se encuentre en el servidor.

<?php

if (!isset($_GET['file']) || empty($_GET['file'])) {
 exit();
}
$root = "archivos/";
$file = basename($_GET['file']);
$path = $root.$file;
$type = '';

if (is_file($path)) {
 $size = filesize($path);
 if (function_exists('mime_content_type')) {
 $type = mime_content_type($path);
 } else if (function_exists('finfo_file')) {
 $info = finfo_open(FILEINFO_MIME);
 $type = finfo_file($info, $path);
 finfo_close($info);
 }
 if ($type == '') {
 $type = "application/force-download";
 }
 // Definir headers
 header("Content-Type: $type");
 header("Content-Disposition: attachment; filename=$file");
 header("Content-Transfer-Encoding: binary");
 header("Content-Length: " . $size);
 // Descargar archivo
 readfile($path);
} else {
 die("El archivo no existe.");
}

?>

Mediante la función basename() hacemos que se devuelva únicamente el nombre del archivo, eliminando cualquier ruta, para que no se puedan descargar archivos de otras carpetas del servidor.

Con la variable $root hemos definido la carpeta donde se encuentran los archivos para descargar.

Acerca de MasterGeek

José Antonio Ramírez # Ingeniero Técnico en Informática de Gestión # Fundador y administrador de nGeeks.com # Market Analyst en SocialRep

También puedes ver...

Cómo tener múltiples instancias independientes de Google Chrome

¿Quieres tener varias instancias de Google Chrome arrancadas simultáneamente y que sean completamente independientes? Es …

63 Comentarios

  1. no me funciona, que podrá ser?
    estoy aplicandolo dentro de un wordpress, exactamente dentro de la carpeta de un theme… tendra algo que ver?

  2. me retracto, todo funcionando.

  3. Sergio N Hernandez

    Excelente codigo…. gracias por publicarlo..

    Saludos

  4. Hola de antemanos gracias. yo he estado probando el codigo y no me fun ciona solo sale el mensaje: El archivo no existe. nose si hay que cambiar algo en el codigo. Por favor necesito ayuda.

    • haber que a mi tambien me paso Carlos, cuando dice que no existe el archivo en $root = “musica/”; (este es mi carpeta) tienes que poner el nombre de la carpeta tal cual la tienes en el servidor, si tiene mayusculas o no. ejemplo: $root = “musica/”; asi esta en el servidor y $root = “Musica/”; y asi lo escribo en download.php habra el error. por eso hay que ponerlo tal cual.

  5. EXCELENTE! Un script que brinda seguridad de forma sencilla a la hora de permitir descargar en un sitio web. Realmente te agradezco muchísimo tu aporte. Funciona maravillosamente!

  6. Funciona perfecto!

    Gracias

  7. me funciona pero cuando descargo un txt descarga con toda la estructura de la pagina y no asi solo con el contenido del archivo. Por que?

  8. Genial… A mi me funcionó perfecto… Muchas gracias por el aporte.

  9. Hola, gracias por el script, solo una consulta, al bajar el archivo, me corta el nombre ejemplo, Naranjo en flor.mp3 lo baja como Naranjo, por lo que lo baja sin formato, no encentro motivo para que corte el nombre, sabrìas decirme por que?
    gracias

  10. Saludos

    Estoy tratando de aprender PHP por mi cuenta, no soy muy experto que digamos, una pregunta, cuando trato de descargar un archivo utilizando tu código, no se me abre el cuadro de diálogo que da la opción de abrir o descargar…. El fichero trata de abrirse en una ventana, si es un archivo de texto se abre pero todo el texto sale todo pegado sin saltos de linea, y si es un pdf pues salen un montón de caracteres incomprensibles…..

    Alguna sugerencia por favor
    gracias

  11. Bueno solo use el código que es el Básico pero quisiera saber el archivo que voy a descargar tiene un limite de peso, mi pregunta va si mi archivo pesa por decir 50mb y tengo 20 de esos tamaños de archivos a mi sitio web ¿No lo hace demasiado pesado? Gracias de antemano por la respuesta y por el código ….. Saludos un amigo que desea aprender y inmiscuir al mundo de la Web … 😀

    • Puede ser demasiado pesado para tu servidor o puede que tu hosting no te permita subir archivos para descargas, pero forzar las descargas no tiene nada que ver, el tamaño de los archivos no influye en eso. Saludos.

  12. Funciona super bien, pero tengo un problema, el archivo que desea descarar es un PDF y cuando me descargar no tiene extensión, solo me descarga con el nombre del archivo y sin la extensión .pdf. Como puedo solucionar eso?

  13. Te aconsejo hacer un ob_clean() y un flush() antes del readfile() para vaciar el buffer de salida ya que PUEDE (probable sobre todo con videos, música, imágenes) que luego el programa con el que los abras no reconozca el formato del archivo.
    Si pruebas con un archivo de texto, veras que el archivo que descargas contiene el HTML de la página + su contenido (con archivos binarios (ej: video) directamente no se abre.

  14. Gracias por el aporte… me funciona super bien

  15. Excelente aporte. completo y muy claro. Tenia dias buscando el código y por alguna razón no funcionaba ninguno.
    Saludos

  16. Excelente brother, de todos los posts que vi este fue el que me funciono a la primera mediante tu script seguro. 100pts…

  17. Hay que cambiar algo en el código? siempre me sale que el archivo no existe y no logro hacerlo funcionar

  18. Excelente código, implementado! Funciona!

    Saludos y gracias!

  19. Cuando ejecuto el link se carga una pagina en blanco con el error “undefined”, si vuelvo a actualizar la pagina con F5 si que me sale la ventana para que elija la descarga o la abertura del fichero.
    Si el link lo ejecuto sin venir de ninguna pagina funciona a la primera. (estoy utilizando las librerias .css y .js jquery para iphone).

  20. tengo un problema, algo en el codigo me bloquea el contenido del body y no me muestra nada :S lo he situado en distintos lugares y nada, ayuda porfa :S

  21. Hola . Gracias por tu aporte!

    En mi localhost funciona correctamente, pero al subirlo al host del cliente deja de funcionar. Alguna idea?

    saludos.

  22. hola buenas… estoy intentando usar este código para un pdf, pero al descargarlo me dice que el archivo es erroneo que tal vez se descodifico mal…. 🙁

  23. Hola!!!
    He probado tu código, pero al descargar el archivo (un pdf) me dice que el archivo está dañado y no se puede reparar..
    qué es lo que hay que arreglar para que el archivo baje bien?
    ya que al subirlo… está bueno, y lo reviso en la carpeta de subidos y también está intacto, pero al momento de descargarlo, baja dañado, espero puedas ayudarme

    saludos

  24. Gracias por este script, me sirvio muchisimo

  25. Funciona Perfecto!!

    Gracias

  26. gracias por el aporte , pero me presente un problema en todos los navegadores, cuando se descarga un archivo pesado, no permite la descarga de 2 o mas archivos, se queda leyendo y no permite otra descarga paralela, ya intente abriendo en otra pagina alguna idea…

  27. gracias por el aporte. En el localhost funciona perfectamente. Ahora una preguntilla al subirlo a un servidor(gratuito) a través de ftp. Ha dejado de funcionar esta descarga. ¿Para descargar archivos desde un servidor hay que hacerlo de otra manera?, o no me funciona por posibles permisos y seguridad del servidor.

    • Puede ser un problema de configuración del servidor.

      Por ejemplo, los hostings compartidos suelen tener configuraciones bastante restrictivas y mucho más si es gratuito.

      Saludos.

  28. veo que hay alguien que ha preguntado lo mismo, en el 24… y no hay respuesta, cachis la mar. bueno si encuentro solución lo pondré por aquí tb… Saludos

  29. Localmente, ustedes tienen control de la dirección de almacenamiento del archivo ($path), sin embargo, cuando utilizan un servidor “ajeno” deberían verificar previamente que el path si sea el que le indican a la página de descarga.
    Saludos !

  30. El código funciona perfecto, pero cuando descargo un archivo RAR en la ventana de dialogo aparece como que es de tipo JPEG ¿Alguien sabe a que se debe? Gracias.

  31. Buenas tengo un problema, cuando descargo el archivo, trato de abrirlo y no tiene formato, a pesar de que la imagen se baja con el nombre que corresponde y con la extencion .jpg
    Que puedo hacer?
    Agradezco la ayuda.

  32. Es un código muy útil,

    Con respecto al comentario #29 si se le incluyen estas dos lineas, los archivos ya no se dañan

    // Descargar archivo

    ob_end_clean();
    flush();
    readfile($file);

    Saludos

  33. En los iphone – safari no funciona. En cambio en los Android se descarga sin problemas. Sabéis cómo se podría solucionar???

  34. me pasa igual que mi conpa~eros

  35. Muchas gracias he!

  36. Gracias!!!!!!!!

  37. Hola,

    Estoy probando estos scripts, que son la caña (felicidades) pero en Chrome (no en IE ni FireFox) obtengo este mensaje por resultado:

    “Se han recibido encabezados duplicados desde el servidor.”

    Alguna idea? Gracias!!
    DaniWeb

  38. el codigo funciona solo miren bn las lineas !!

    Gracias

  39. Hola. Lo probe local y me funciona perfecto. En el servidor en vez de descargar la imagen, veo la misma pero el codigo detras de esta. Alguna idea?

  40. A mi me funciona bastante bien, he probado con varios tipos de archivos pero solo el .rar y el .doc se los descarga directamente. Los .pdf los abre en el navegador y con el mp3 sale la pantalla en blanco.
    Pero lo que a mi me interesa son los .doc ó los .pdf porque he creado de forma dinámica este enlace:
    echo “$t_descargar“;
    Necesito que me cree la factura que se va a descargar (para no tener que hacerla yo manualmente)
    ¿alguien puede decirme como se hace?

  41. Funciona muy bien; cuando cambies

    header(“Content-Disposition: attachment; filename=$file”);
    por esta:
    header(“Content-disposition: attachment; filename=\”$file\””);

    Fijate en las comillas deben de ser iguales si no marca error.

  42. Super útil.
    No olviden hacer las modificaciones de los comentarios
    #40 y #50.
    Muchas gracias.

  43. Hola!

    Todos los .doc y .rtf que me intento descargar aparecen erróneos, caracteres raros, vamos, los descarga corruptos.¿Poruqé puede ser?

  44. no se en que me equivocado no me fuciona el codigo

    y en download.php

    quiero que descargue documentos ese serie el codigo tambien
    o me pueden decir que codigo seria por favor
    y el dow

  45. que codigo seria para descargar documentos llamarlo desde mi apgina web por favor

  46. Buenas he probado vuestro código con todo tipo de archivos y con todo tipo de cabeceras y con todas las opciones que ibais poniendo en los comentarios y mi resultado a sido todo el contrario fuerza la apertura del fichero en el propio navegador (chrome, firefox, opera, ie 11, maxthon, safari) al final he terminado por sospechar que es cosa de 1and1 que es mi hosting, podéis decirme algo al respecto?

    • Me auto respondo ya que parece ser que nadie le ha pasado lo mismo nada mas que a mi.

      Al final no es culpa del hosting, si no de la codificación del php, lo he guardado en ANSI y todo solucionado.

  47. Funciona bien, pero cuando descargo los archivos estos pesan 2kb con error de apertura, la función si ubica el archivo ya que si lo borro del servidor me marca el error “el archivo no existe”, hay alguna razon por la que no se descarguen bien?.

  48. el codigo final deberia ser:

  49. Tu script funciona muy bien al poner $root = “archivos/”; y descarga la foto.jpg pero al querer descargar un archivo del subdirectorio archivos/mp4/video.mp4 no lo descarga y si el video lo pongo en la raiz archivos si jala pero dentro de la subcarpeta ya no

    como puedo resolver eso de que tambien descarge archivos/subcarpeta/video3.mp4

    por ejemplo

  50. Hola todos, el código funciona de maravilla pero me he llevado una sorpresa al ver que el crome oculta todos los enlaces de descarga. ¿me podéis ayudar?.
    Con todos los navegadores funciona, incluso safari, menos crome.
    Saludos


  51. zknner:

    Tu script funciona muy bien al poner $root = “archivos/”; y descarga la foto.jpg pero al querer descargar un archivo del subdirectorio archivos/mp4/video.mp4 no lo descarga y si el video lo pongo en la raiz archivos si jala pero dentro de la subcarpeta ya no
    como puedo resolver eso de que tambien descarge archivos/subcarpeta/video3.mp4
    por ejemplo

    amigo a mi no me descarga de ninguna forma noc como colocarlo para que me descargue de la carpeta public_html de mi hosting

  52. me pareció más fácil este.. para entornos no agresivos ahhaha

  1. Pingback: Bitacoras.com

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *