Forcer le téléchargement d’un fichier

Publié dans PHP | Marqué avec ,
Share

Lorsque j’ai conçu la Bnbox v.3 j’aurais bien aimé savoir ce que j’ai découvert il y a peu. Comment forcer le téléchargement d’un fichier sur un site Web ? Par exemple un fichier PDF (so beautiful!) ou un fichier mp3 sont en général directement lus par les navigateurs récents. Mais il peut être pratique, parfois, d’éviter au visiteur la manipulation Clic droit > Enregistrer la cible du lien sous….

<?php
/**
 * Force le téléchargement d'un fichier
 * 
 * @param string $filePath Emplacement du fichier sur le serveur web
 * @param string $fileNameToDl=<em> Nom du fichier que l'utilisateur va télécharger (si on souhaite le rendre différent de celui d'origine)
 * @post Ouvre une fenêtre permettant le téléchargement du fichier
 * /
function forcerTelechargement($filePath, $fileNameToDl=</em>)
{
    header('Content-Description: Téléchargement le fichier '.$fileName);
    header('Content-Type: application/octet-stream'); // On peut mettre text/gif, etc...
    header('Content-Length: '.filesize($path.'/'.$fileName));
    header('Content-disposition: attachment; filename='.($fileNameToDl != NULL ? $fileNameToDl : basename($filePath))); // Nom du fichier téléchargé par l'utilisateur. On peut donc mettre ce qu'on veut.
    header('Pragma: no-cache');
    header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date dans le passé pour ne vraiment pas avoir de cache
    readfile($filePath); // Ouvre le fichier dans le buffer : d'après le geader, ce sera une fenêtre de téléchargement
    exit();
}

forcerTelechargement('./document/exemple.pdf');


Pour cette fonction, je m’inspire pas mal d’un billet (du même nom que le mien) sur Apprendre PHP, mais j’ai essayé de comprendre un peu mieux tous les paramètres que l’on fournit à la fonction header, notamment en lisant la documentation PHP pour header et readfile. Mais il faut avouer que le manuel est plutôt avare à ce sujet (pour une fois), ou alors c’est moi qui cherche au mauvaise endroit.

  • Content-Description : la description qui apparaitra lors du téléchargement.
  • Content-Type : type du fichier à télécharger. Si je comprend bien, application/octet-stream fonctionne toujours, mais il peut être intéressant pour éviter tous problèmes, de préciser le type. Pour faire cela automatiquement, il faut voir du côté de pathinfo (qui est vraiment une fonction magique !) et la compléter avec un switch pour avoir le type sous la bonne forme. (je n’ai pas encore trouvé une fonction renvoyant directement application/octet-stream)
  • Content-Length : taille du fichier en octet. Un petit filesize fera l’affaire.
  • Content-disposition: attachment; \[filename=nom_fichier] : c’est le mot clef attachment qui fait en sorte qu’il y ait une fenêtre de téléchargement. En effet, avec Content-disposition, on précise le buffer de sortie, attachment pour une fenêtre de téléchargement, inline pour la fenêtre principale. Quant à filename, il permet de spécifier, si on le souhaite, un nouveau nom pour le fichier que l’utilisateur va télécharger.
  • Pragma : aucune idée. Mais apparemment la gestion du cache dépend beaucoup du navigateur, donc il vaut mieux préciser partout que l’on ne souhaite pas de cache.
  • Cache-Control : permet de gérer la mise en cache
  • Expires : en fournissant une date dans le passé, on est de plus en plus sûr de ne pas mettre en cache Maintenant que l’on a précisé le buffer de sortie, le nom du fichier que l’utilisateur va télécharger, et le type de ce fichier, il n’y a plus qu’à ouvrir le fichier dans le buffer en question avec readfile.

Et voilà le travail ! Il n’y a plus qu’à mettre tout ça dans un fichier PHP, et à faire un système pour récupérer le nom du fichier à télécharger à l’aide d’une variable GET ou POST. Il serait peut-être bon d’ailleurs d’avoir une table SQL listant les fichiers qu’il est possible de télécharger, pour éviter que l’utilisateur puisse télécharger n’importe quoi simplement en donnant une valeur de variable GET ou POST de son cru.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *