Forzar descarga de ficheros en lado cliente

 
Por regla general los exploradores de Internet muestran las páginas con contenido ASCII (texto plano) y en el caso de que su contenido sea binario, la muestran en caso de que las "conozcan", por ejemplo los JPG, GIF ó PNG, en el caso de tener instalado Flash o la máquina virtual de java o PDF navegarían a estos archivos o sacaría el mensaje informativo de descarga en caso de no tenerlos, o por ejemplo un documento de Word en Internet Explorer lo mostraría mientras que en otros navegadores mostraría el mensaje de descarga.

Sin embargo en muchas ocasiones, podemos querer forzar a que un archivo siempre se descargue independientemente del tipo de archivo que sea. Para estos casos podemos informar al explorador de que el archivo que le enviamos es para descargar. Esto se hace con las cabeceras del documento, concretamente:

header("Content-type: application/force-download");


El artículo de ejemplo recoge el nombre del archivo a descargar por URL, con la variable "doc", el único cambio que habría que hacer en el PHP es pasar la dirección del directorio de "descargas". El documento a descargar se coge de la unión del directorio de descargas y el nombre del documento pasado por URL.
Por motivos de seguridad se reemplazan los dos tipos de barras separadoras de directorios en diferentes sistemas operativos por "_". Esto evita que usuarios malintencionados "jueguen" con las variables pasadas y se descarguen documentos que no sean públicos.

También se podría hacer que en vez de pasar el archivo mediante una variable, escribir el archivo a mano en el PHP, o pasar una variable tratada con anterioridad, reemplazando la línea:

$sDocumento = $sUrlDescargas.str_replace($vBarras, "_", $_GET["doc"]);

por (escribiendo el archivo a mano):

$sDocumento = $sUrlDescargas."archivo_a_descargar.zip";

o (recogiendo el dato de una variable de php):

$sDocumento = $sUrlDescargas.$variable;


Por último, recordar que no se debe de enviar ningún dato antes de las cabeceras (header), tener cuidado, ya que es muy típico el error de introducir antes del código un salto de línea o un espacio y con esto ya daría un error de tipo Warning: Cannot modify header information - headers already sent by (output started ...


HTML:
<!--Esto cambiaría el nombre del archivo a descargar a ".._index.php".-->
<a href="descargar.php?doc=../index.php">Pincha para descargar</a><br>
<!--Descarga dibujo.bmp del directorio de descargas-->
<a href="descargar.php?doc=dibujo.bmp">Pincha para descargar</a>

Código:
<?php
//Copyright © McAnam.com

    $sDirectorio = "/descargas/"; //Introducir directorio de descargas
    $sUrlDescargas = $_SERVER["DOCUMENT_ROOT"].$sDirectorio;
    $vBarras = array("/", "\\");
    $sDocumento = $sUrlDescargas.str_replace($vBarras, "_", $_GET["doc"]);
    
    if (file_exists($sDocumento))
    {
        header("Content-type: application/force-download");
        header("Content-Disposition: attachment; filename=".basename($_GET["doc"]));
        header("Content-Transfer-Encoding: binary");
        header("Content-Length: ".filesize($sDocumento));
        readfile($sDocumento);
    }
    else
    {
        echo "<br>Ha sido imposible descargar el fichero";
    }
    
?>

Links relacionados:
Recorrer directorio para tratar su contenido
Leer líneas de un archivo con la función file
Compresión de ficheros con PHP a GZIP
Variables globales de servidor con $_SERVER


Para cualquier duda, consulta, sugerencia, opinión, colaboración, etc; no dude en ponerse en contacto con nosotros

Copyright © 2002-2017 [McAnam]. Reservados todos los derechos.
www.mcanam.com