Forzar descarga con PHP

  • 05/10/2008
  • 12:12 am
  • unijimpe

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.

CODE:
  1. 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.

PHP:
  1. $name = $_GET['file'];
  2. header("Content-disposition: attachment; filename=$name");
  3. header("Content-type: application/octet-stream");
  4. 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.

PHP:
  1. // File: download.php
  2. if (!isset($_GET['file']) || empty($_GET['file'])) {
  3.     exit();
  4. }
  5. $root = "img/";
  6. $file = basename($_GET['file']);
  7. $path = $root.$file;
  8. $type = '';
  9.  
  10. if (is_file($path)) {
  11.     $size = filesize($path);
  12.     if (function_exists('mime_content_type')) {
  13.         $type = mime_content_type($path);
  14.     } else if (function_exists('finfo_file')) {
  15.         $info = finfo_open(FILEINFO_MIME);
  16.         $type = finfo_file($info, $path);
  17.         finfo_close($info)
  18.     }
  19.     if ($type == '') {
  20.         $type = "application/force-download";
  21.     }
  22.     // Set Headers
  23.     header("Content-Type: $type");
  24.     header("Content-Disposition: attachment; filename=$file");
  25.     header("Content-Transfer-Encoding: binary");
  26.     header("Content-Length: " . $size);
  27.     // Download File
  28.     readfile($path);
  29. } else {
  30.     die("File not exist !!");
  31. }

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.

Posts Relacionados

Total de Comentarios: 7

Publicidad
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!

14/10/2008
11:15 am

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

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
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.

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
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
11/11/2008
10:44 am

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

Gracias - Tato

Enviar Comentario

(*)

(*)