Forzar descarga con PHP

En algunas ocasiones deseamos que determinado archivo sea para descarga y no para visualizarlo directamente en el browser, en esta ocasión les presentamos la forma para forzar la descarga de archivos con PHP.

Normalmente cuando se accede a archivos desde un browser se envian headers indicando el tipo de archivo con Content-type, adicionalmente se puede emplear el header Content-disposition para indicar como mostrar el archivo, por ejemplo para indicar que estamos enviando el archivo photo.jpg como adjunto el header sería.

Content-disposition: attachment; filename=photo.jpg

Forzar descarga (Básico)
Basado en estos conocimientos previos podríamos crear un script para forzar la descarga de cualquier archivo, para ello suponemos que el nombre del archivo lo pasamos por parámetro GET en la variable file.

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

Con esto ya tenemos nuestro script para descargar archivos, por ejemplo si tenemos una imagen llamada img.jpg, para descargarla podríamos acceder a download.php?file=img.jpg para descargarla.

Forzar descarga (Avanzado)
El script anterior tiene varios problemas, el primero es que alguien podría descargar cualquier archivo de nuestro servidor, por que se puede poner la ruta incluyendo carpetas en la variable file, por ejemplo download.php?file=../index.php, el segundo problema es que no hemos verificado si el archivo existe. Entonces vamos a agregar las reglas necesarias para limitar la descarga a una carpeta y verificar si existe el archivo.

// File: download.php
if (!isset($_GET['file']) || empty($_GET['file'])) {
	exit();
}
$root = "img/";
$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";
	}
	// Set Headers
	header("Content-Type: $type");
	header("Content-Disposition: attachment; filename=$file");
	header("Content-Transfer-Encoding: binary");
	header("Content-Length: " . $size);
	// Download File
	readfile($path);
} else {
	die("File not exist !!");
}

Nótese que hemos utilizado la función basename el cual devuelve el nombre del archivo, eliminando alguna ruta existente, con este prevenimos que se intente acceder a otra carpeta. La carpeta donde están los archivos para descargar lo definimos en la variable $root, en este caso img/. Luego obtenemos el tamaño del archivo y el tipo de archivo para finalmente enviar los headers para indicando la descarga.

Pueden ver el ejemplo funcionando en phpdownload/download.php, por ejemplo tenemos el archivo img01.jpg en la carpeta img, entonces para descargar estos archivos podremos hacerlo accediendo a download.php?file=img01.jpg. Si intentamos descargar download.php?file=../html/img01.jpg devolverá el mismo archivo pues no permite que se pueda cambiar de directorio.

Comentarios Total 45 comentarios

jordi
Publicado: 14/10/2008 11:13 am

Hola,
Aclarar que soy novato en esto del PHP, como podreis ver. El caso es que estoy intentando crear un script que me permita descargar un archivo u otro segun si la checkbox del formulario de una pagina precedente ha sido selecionado o no.
Buscando por ahi he acabado haciendo esto:
$checkbox=$_POST[‘checkbox’];

if ($checkbox ==’si’)
{
$archivo = «formsolicitud/formulario_solicitud_plus.zip»;
}
else
{
$archivo = «formsolicitud/formulario_solicitud.zip»;
}
?>

El caso es que, las diversas pruebas que he hecho (tambien con otros scripts) siempre me da problemas con los header.
Me aparece un mensaje del tipo:
Warning: Cannot modify header information – headers already sent by (output started at /homepages/11/d221028650/htdocs/funding-it/solicitar4.php:6) in /homepages/11/d221028650/htdocs/funding-it/solicitar4.php on line 129

Podriais echarme una mano?
Tal vez me estoy complicando y se puede resolver con un script mas simple…
Gracias de antemano!

jordi
Publicado: 14/10/2008 11:15 am

Ups…!
Me habia olvidado esta parte del script, al final del que os he indicado:

jordi
Publicado: 14/10/2008 11:17 am

pues no aparece!!
Lo siento pero no aparece la parte del script que tiene los header…

De todos modos acepto cualquier sugerencia.

dami
Publicado: 02/11/2008 1:39 pm

Hola amigos,
Me pregunto si se puede hacer que la pag en lugar de descargar archivos descargue una carpeta.

La cosa es asi, tengo una carpeta en mi hosting que tiene dentro un monton de imagenes y mi idea es que en lugar de hacer un script que descargue una por una las imagenes directamente me descargue la carpeta.

Por lo que estuve viendo en php es imposible pero me niego a creerlo.
alguien sabe como hacerlo?

Gracias.

unijimpe
Publicado: 02/11/2008 2:10 pm

Hacer la descarga de múltiples archivos no es una solución adecuada, la recomendación es primero convertir toda esta carpeta en un ZIP y luego forzar la descarga, para una guía de como crear archivos ZIP puedes leer: Crear archivos ZIP con PHP, entonces si quieres agregar todos los archivos de la carpeta al ZIP puedes hacer un script que liste y agregue todos los archivos al ZIP que estas creando y posteriormente forzar la descarga.

dami
Publicado: 02/11/2008 2:46 pm

Gracias amigo por la respuesta,
Lo que propones parece ser la mejor opcion, pero lei que al comprimir archivos se consume nucha memoria del servidor, y no se si mi hosting lo va a soportar o me lo va a permitir.
pero es cuestion de probar!

saludos

Tato
Publicado: 11/11/2008 10:44 am

Hola, quisiera saber como puedo controlar que el archivo fue exitosamente descargado.

Gracias – Tato

EDU
Publicado: 16/09/2009 4:53 pm

me sale este error, que sera?

Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator, webmaster@mbaconsultores.org and inform them of the time the error occurred, and anything you might have done that may have caused the error.
More information about this error may be available in the server error log.


Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.


--------------------------------------------------------------------------------


Apache/2.2.11 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.8e-fips-rhel5 mod_auth_passthrough/2.1 mod_bwlimited/1.4 mod_perl/2.0.4

juan
Publicado: 06/11/2009 1:30 pm

hola lo puegue en mi wamp tal cual pero no muestra nada le tengo que modificar algo?ni error ni nada solo una pag en blanco.

diseño web
Publicado: 14/11/2009 10:05 pm

Tienes que pasar la variable get ‘file’, para que luego pueda ser recuperada con $_GET[‘file’]

juan
Publicado: 23/11/2009 10:34 pm

hola y gracias por dejar descargar tus scripts, pero ahora tengo algo que me aqueje estoy haciendo una pagina eb que se ve rebien en mozila y ya avnce algo pero a la hora de verolo en ie todo crashea, no se ve algunos botones no aceota codigos polaroid etc que puedo hacer para solucionar esto

diego morales
Publicado: 26/11/2009 7:55 pm

Buen día me ha surgido un problema, al ejecutar el script todo anda de lujo pero al descargar el archivo e intentar abrirlo en mi maquina me manda aviso que el archivo esta dañado me podrian ayudar con esto alguien tiene una idea de que esta pasando???

bonrtokill
Publicado: 05/12/2009 4:12 pm

tengo un problema para forzar las descargas ya que el script que tengo no es compatible con header.
aqiu sta el codigo del download.php?serie=

bonrtokill
Publicado: 05/12/2009 4:12 pm

$link = mysql_connect($host, $user, $pass);
mysql_select_db($db, $link);
$serie=$_GET[serie];
$result=mysql_query(«SELECT * FROM imagenes WHERE serie=’$serie'»);
$row=mysql_fetch_row($result);

bonrtokill
Publicado: 05/12/2009 4:15 pm

entonces como debo forzar la descarga

unijimpe
Publicado: 05/12/2009 5:52 pm

bonrtokill si a lo que te refieres es a descargar un archivo cuyo nombre lo extraes de una base de datos el script no es complicado:


$link = mysql_connect($host, $user, $pass);
mysql_select_db($db, $link);
$serie = $_GET['serie'];
$result = mysql_query("SELECT * FROM imagenes WHERE serie='$serie'");
$row = mysql_fetch_row($result);


$name = $row['imagen'];
header("Content-disposition: attachment; filename=$name");
header("Content-type: application/octet-stream");
readfile($name);

Robback
Publicado: 29/12/2009 5:05 am

Hola soy nuevo en el post. Leí y descargué tu archivo y no lo he usado pero creo que va a servir. Quisiera saber cual seria el codigo para descargar múltiples archivos a apartir de un solo archivo PHP es decir que el usuario elija el link que se comunique con el php y lo descargue por ejemplo

bajar imagen.jpg
bajar logo.png
bajar flash.swf

Como verais el archivo php es el mismo pero el archivo diferente. Por favor decirme cómo le hago muchas gracia.

Saludos.

Vladimir
Publicado: 12/01/2010 9:35 pm

Amigo Robback, no se si ya lo solucionaste pero la única solución es poner los tres archivos dentro de la misma carpeta, nada más…
Saludos

JERICO
Publicado: 10/02/2010 1:57 am

HOLAAA unijimpe
el codigo que pusiste, lo use pero da el siguiente error
link = mysql_connect($host, $user, $pass);
mysql_select_db($db, $link);
$serie = $_GET[‘serie’];
$result = mysql_query(«SELECT * FROM imagenes WHERE serie=’$serie'»);
$row = mysql_fetch_row($result);

$name = $row[‘imagen’];
header(«Content-disposition: attachment; filename=$name»);
header(«Content-type: application/octet-stream»);
readfile($name);

y el error es este

Warning: Cannot modify header information – headers already sent by (output started at /home/metalico/public_html/portal/config.php:7) in /home/metalico/public_html/portal/descarga.php on line 8

Warning: Cannot modify header information – headers already sent by (output started at /home/metalico/public_html/portal/config.php:7) in /home/metalico/public_html/portal/descarga.php on line 9

Warning: readfile() [function.readfile]: Filename cannot be empty in /home/metalico/public_html/portal/descarga.php on line 10

JERICO
Publicado: 10/02/2010 1:59 am

YO USO ESTE EN EL LOCAL HOST Y FUNSIONA DE MARAVILLA

include(«config.php») ;
if(is_numeric($_GET[‘id’])) {
$resp = mysql_query(«select mp3 from bajados where id='».$_GET[‘id’].»‘») ;
if(mysql_num_rows($resp) != 0) {
$datos = mysql_fetch_array($resp) ;

$datos[mp3].»/».$datos[titulo];
header(«Content-disposition: attachment; filename=\»».basename($datos[mp3]).»\»»);
readfile($datos[mp3]);

**// pero no funsiona en la pagina!! me da el mismo error que te dije!!

jUANM
Publicado: 03/03/2010 2:49 pm

HOLA QUE TAL A VER SI ME PUEDES ECHAR UNA MANO HELP… AL PARECER LA DESCARGA SE REALIZA 100% PERO EL INCONVENIENTE ES QUE CUANDO TRATO DE ABRIR DICHO ARCHIVO NO PUEDO, REVISO LAS PROPIEDADES DEL ARCHIVO DESCARGADO Y ME APARECE QUE EL TAMAÑO ES 0, NO ENTIENDO EL PORQUE Y ESA ES MI INQUIETUD…..
POR LA ATENCION MIL GRACIAS

aprendiz2
Publicado: 02/04/2010 10:41 pm

que onda wei soi novato es esto tambien i me interesa mucho el tema..
aja a ver un exelente experto en defacing que me ayude en esto
lo primero es poner el scrip en el apache en www con el nombre index.php adentro del index solo el scrip.. luego ir a cualquier web i poner algo asi

ejemplo
http://blog.unijimpe.net/download.php?file=imgen.jpg

asi es como se debe hacer o que hago con ese codigo del scrip lo pongo todo en el navegado i listo

estoi confundido

ayudaa pliss

José María
Publicado: 19/04/2010 3:42 pm

Muy buenas, yo ando peleado con el siguiente código, tengo almacenados distintos tipos de archivos en variables de la bbdd, bien si hago referencia a uno que contiene un pdf lo abre sin problema, detecta que es un pdf, eso si no es capaz de sacar el nombre real del pdf, pero el problema es cuando hago referencia a otra variable que contiene un word x ejemplo, además de no sacar el nombre real no sabe que extensión tiene, os adjunto el código por si alguien me puede ayudar.

Saludos

José María
Publicado: 19/04/2010 3:47 pm

<?php
include ('bbdd.inc.php');
$result10 = mysql_query('SELECT * FROM visitas', $conexion);
while ($row10 = mysql_fetch_array($result10))
{
$visitas2=$row10[visitas];
}
$result = mysql_query("SELECT * FROM ficheros", $conexion);
while ($row = mysql_fetch_array($result))
{
$tarifas_castellano=$row[campo1];
$tarifas_catalan=$row[campo2];
$tarifas_ludoteca_esp=$row[campo3];
$tarifas_ludoteca_cat=$row[campo4];
}
$root = 'pdf/';
$file = basename($_GET['$tarifas_ludoteca_esp']);
$path = $root.$tarifas_ludoteca_esp;
$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';
}
// Set Headers
header('Content-Type: $type');
header('Content-Disposition: attachment; filename=\'$file\'');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . $size);
// Download File
readfile($path);
} else {
die('File not exist !!');
}

?>

Norma
Publicado: 04/06/2010 9:11 am

Hola , quiero consultarles como hago para que en la variable $root esta la ruta de una pc que esta en red en windows y accedo poruna direccion ip?
busca muchao en la web pero no encuentro la respuesta.
Gracias

Edgar
Publicado: 09/07/2010 1:22 pm

Me funcionó perefcto.
Gracias doc.

ERWIN
Publicado: 22/07/2010 6:25 pm

Buenas tardes a todos,

Tengo un problema al momento de abrir el archivo descargado me sale que el archivo puede estar dañado o no funciona

Alguien me puede ayudar

javier
Publicado: 24/07/2010 2:05 pm

Hola que tal unijimpe, oye una pregunta: como le puedo hacer para que solo se puedan descargar archivos de x tipo y no cualquiera.

Saludos!!!

unijimpe
Publicado: 24/07/2010 2:16 pm

Javier, lo que podrías hacer para permitir descargar de determinados tipos, es verificar la extensión de los archivos que se esta intentando descargar, si no coincide con el archivo permitido, cancelas la descarga.

Por ejemplo si solo deseas permitir que se descarguen las imágenes del tipo JPG y GIF, tendríamos el siguiente código:

$ext = substr($file, strrpos($file, '.')+1);
if ($ext != "gif" && $ext != "jpg") {
exit;
}

DjMiki
Publicado: 09/12/2010 6:13 pm

Hola otra vez UNIJIMPE, ahm bueno como siempre leyendo tu blog y pa comentarte q tu script me ayudo mucho nuevamente, pero ahm no era precisamente lo que queria ya que yo descargo archivos de otro dominio, que esta dentro del mismo hosting con rutas relativas, en fin mi problema rapidca en la parte de los HEADERS precisamente en «Content-Disposition: attachment; filename=». Mi problema es que el nombre del archivo contiene espacios y al momento de enviar el archivo solo me muestra la primera palabra antes del primer espacio, por EJM.: «hola que tal» solo manda «hola», y estoy probando varias formas y nnguna es la que deseo, quizas tu puedas ayudarme, eh utilizado URLENCODE() pero me da con simbolos «+», HTMLENTITIES() no funciona, HTMLSPECIALCHARS(), y tampoco funciona, tons quisiera saber que forma puedo utilizar, ojala me puedas ayudar.
Gracias de Antemano.

unijimpe
Publicado: 09/12/2010 10:55 pm

Para no tener problemas, los nombres de los archivos deberían estar bien formateados (Sin tildes, espacios en blanco). Ahora si quieres generar un nombre de archivo que no contenga estos caracteres podrías utilizar la función que se explica en http://blog.unijimpe.net/convertir-texto-en-url-seo/ la cual elimina los espacios en blanco, tildes entre otros.

jonvel
Publicado: 22/03/2011 9:46 am

Lo ejecuto; y cuando lo ejecuto la pantalla me aparece en blanco como a otros; como soluciono eso??
Muchas Gracias..

bletron
Publicado: 24/03/2011 3:59 pm

El ejemplo que se propene es muy bueno pero les recomiendo que vean el siguiente código funcionando aquí siguiendo el link encontraran un forzado de descarga funcionando y además podran descargar el código.

Maxx Calis
Publicado: 14/08/2011 5:04 pm

Hola que tal… muchas gracias por compartir tu conocimientos, descargue el script, pero no me funciona, ya que intento descargar un .jpg, pero este aparece como dañado (0 byte)
Cual sería el problema?

juan
Publicado: 15/08/2011 2:49 pm

Hola. El script funciona sin problemas para bajar una imagen jpg, pero si le pongo un .rar me da error: En firefox Firefox no puede encontrar el archivo en descargar.php .. y en chrome Error 6 (net::ERR_FILE_NOT_FOUND): No se ha podido encontrar el archivo o el directorio.

Pero el archivo existe y si pongo directamente la URL al archivo en el server lo baja sin problemas.

Qué puede estar pasando?

Gracias!

cesar
Publicado: 15/09/2011 10:09 am

Gracias amigo, todo funciono muy bien

Andyricor
Publicado: 29/09/2011 6:12 am

genial!!

Gabriel
Publicado: 17/02/2012 11:02 am

Lo que me descarga es el download.php no la imagen. Por qué ocurre esto. Puedes darme una solución.

Gastón
Publicado: 09/04/2012 3:35 pm

Muchas gracias! funciona excelente!
Saludos!

Alec
Publicado: 31/05/2012 11:38 am

Pero mi problema es que quisiera forzar la descarga de un link o enlace externo.

si existe un link externo a mi web que sea por ejemplo.

http://goxex.miweb.com/files/ejemplo.jpg

Ese archivo no está en mi web sin embargo pongo ese link pero quiero que la descarga sea forzada. ¿Como puedo hacer?

anonimo
Publicado: 23/07/2012 7:27 pm

ten cuidado en los headers que pongas……

Guillermo
Publicado: 09/11/2012 10:31 pm

no me funciona no se si hay que cambiar algo en el codigo peor siempre me tira este error: File not exist !! yo copie el codigo tal cual y cree la careta img pero no da!!!

Vitho - Gif animados
Publicado: 18/03/2013 9:58 pm

Me funciono perfectamente :D

Muchas gracias por compartir

Jorge
Publicado: 28/04/2013 1:39 pm

El script me funciona perfectamente, además como lo uso puntualmente para que el usuario se baje un catálogo, ni siquiera lo paso por parámetros.

Pero tengo una duda. En Internet Explorer no se fuerza la descarga, le da la posibilidad al usuario de bajárselo o visualizarlo. En otros navegadores como Chrome pueden rechazarlo también.

Necesitaría poder manejar estas situaciones. Es decir, si un usuairo se descarga el archivo, que le redireccione a una página dónde se le agradezca ese hecho. Si prefiere visualizarlo, que lo visualize.

Entiendo que dependerá de cada navegador.

He probado con:

header ("location:gracias.html");

en cuyo caso se va a la página gracias.html sí o sí.

Cristian Araya
Publicado: 14/07/2015 7:20 am

utilicé el código para gestionar las descargas de archivos y en principio funciono a la perfección, la rutina es mas o menos esta, el usuario selecciona un año, mes, y programa de trabajo, la aplicación lista las actividades asociadas a ese periodo con su respectivo archivo de respaldo de cumplimiento de dichas actividades (están fueron subidas al servidor por el usuario) al tratar de descargar ha comenzado a pasar que las descargas se interrumpen provocando errores. No creo que sea error del código, me da la idea que puede ser algo en el servidor, pero no había pasado por esto así que no se que pueda estas pasando, alguna idea?

 

Comentar

En este blog los comentarios están moderados, serán mostrados cuando el administrador los apruebe. Por favor, evita comentarios ofensivos u obscenos por que no serán aprobados.
Si deseas publicar código fuente debes hacerlo entre las etiquedas <code> y </code>, además debes reemplazar los carácteres < por &lt; y > por &gt;.

(Requerido)

(Requerido, no será publicado)

(Requerido)

(Tags aceptados: <a> <em> <strong> <code> <ul> <li>)