7.PHP pour le web

7.5.Upload de fichier

7.5.1.Présentation

L'upload de fichier consiste à transférer un fichier de l'ordinateur de l'utilisateur vers le serveur web (il s'agit de l'opération inverse du téléchargement ou terme anglais download). Ceci peut vous permettre de proposer à un utilisateur de mettre en ligne des photos, des images.

7.5.2.Le formulaire

L'upload de fichier s'appuie tout simplement sur le protocole fourni par le navigateur. La première étape consiste donc à mettre en place un formulaire proposant un bouton, autorisant l'utilisateur à sélectionner un fichier sur son disque dur. Cela se fait grâce au bout de code HTML qui suit:
<html>
  <body>
    <form enctype="multipart/form-data" action="fileupload.php" method="post">
      <input type="hidden" name="MAX_FILE_SIZE" value="100000" />
      Transfère le fichier <input type="file" name="monfichier" />
      <input type="submit" />
    </form>
  </body>
</html>
Ce formulaire doit comporter les quelques spécificités suivantes:
  • utilisation de la method="post" la seule permettant le transfert de données volumineuses
  • une option enctype="multipart/form-data" indiquant un échange de données
  • un champ caché (optionnel) indiquant la taille maximale (en octets) autorisée pour le fichier à "uploader"
  • un champ de type fichier permettant entre autre à l'utilisateur de sélectionner le fichier dans son système de fichiers.
  • il doit renvoyer (champ action) vers un script PHP capable de "réceptionner" le fichier (il s'appelle ici fileupload.php)
rem
  • La taille maximale autorisée est également contrainte par la configuration du serveur web et de php (Cf. fichier php.ini)
Mais que devient le fichier transféré sur le serveur web? Qu'est-ce que je dois mettre dans le fichier fileupload.php? C'est ce que nous allons voir.

7.5.3.Réception des données coté serveur

Une fois, le fichier sélectionné et le bouton de validation du formulaire pressé, le navigateur va lire le contenu du fichier, le formatter et l'envoyer au serveur web (dans une requête HTTP). Le serveur web le stocke alors dans un espace temporaire sous un nom arbitraire.
Le "moteur" PHP fourni alors de nouvelles informations dans un tableau $_FILE[] (depuis PHP 4.1). Ce tableau contient alors un tableau associé à une clé de nom indique ) celui qui a été fixé dans le champ de type file du formulaire (ici nous ferons donc appel à $_FILES["monfichier"]).
Ce tableau contient les clés suivantes:
  • tmp_name qui contient le nom (et chemin) du fichier temporaire sous lequel le fichier a été stocké.
  • name qui contient le nom qu'avait le fichier dans l'espace de l'utilisateur
  • size qui contient la taille du fichier en octets
  • type qui contient le "MIME type" du fichier (ex: text/html, image/gif, etc.)
Le nom et chemin du fichier temporaire est donc donné par $_FILES["mon_fichier"]["tmp_name"].
Après avoir testé la présence du fichier et s'être assuré qu'il s'agit bien d'un fichier uploadé (via la fonction is_uploaded_file()), il faut copier ce fichier dans un espace non temporaire que vous aurez défini. D'où le script (portant le nom précisé dans le champ action du formulaire) suivant:
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
<?php
// Copie dans le repertoire du script avec un nom
// incluant l'heure a la seconde pres 
$repertoireDestination = dirname(__FILE__)."/";
$nomDestination        = "fichier_du_".date("YmdHis").".txt";

if (is_uploaded_file($_FILES["monfichier"]["tmp_name"])) {
    if (rename($_FILES["monfichier"]["tmp_name"],
                   $repertoireDestination.$nomDestination)) {
        echo "Le fichier temporaire ".$_FILES["monfichier"]["tmp_name"].
                " a été déplacé vers ".$repertoireDestination.$nomDestination;
    } else {
        echo "Le déplacement du fichier temporaire a échoué".
                " vérifiez l'existence du répertoire ".$repertoireDestination;
    }          
} else {
    echo "Le fichier n'a pas été uploadé (trop gros ?)";
}
?>
</body>
</html>
    
rem
  • Pour des raisons de simplicité, le fichier a été renommé sous un nom comportant la date et l'heure à la seconde près. Cela est largement suffisant pour faire quelques tests mais ne doit pas être utilisé sur un "vrai" site où il peut très bien y avoir plusieurs fichiers envoyés à la même seconde.
  • Egalement pour ne pas alourdir l'exemple, l'extension du fichier a été fixée à ".txt" alors que le fichier envoyé était peut-être d'un autre type.
  • En fait, le test is_uploaded_file() et le déplacement du fichier par rename() peut être réalisé par un unique appel à move_uploaded_file().
Nous allons, via un script un poil plus complexe améliorer ces derniers points (Pensez à le renommer en fileupload.php ou a modifier votre script fileupload.html).
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
<?php
$nomOrigine = $_FILES['monfichier']['name'];
$elementsChemin = pathinfo($nomOrigine);
$extensionFichier = $elementsChemin['extension'];
$extensionsAutorisees = array("jpeg", "jpg", "gif");
if (!(in_array($extensionFichier, $extensionsAutorisees))) {
    echo "Le fichier n'a pas l'extension attendue";
} else {    
    // Copie dans le repertoire du script avec un nom
    // incluant l'heure a la seconde pres 
    $repertoireDestination = dirname(__FILE__)."/";
    $nomDestination = "fichier_du_".date("YmdHis").".".$extensionFichier;

    if (move_uploaded_file($_FILES["monfichier"]["tmp_name"], 
                                     $repertoireDestination.$nomDestination)) {
        echo "Le fichier temporaire ".$_FILES["monfichier"]["tmp_name"].
                " a été déplacé vers ".$repertoireDestination.$nomDestination;
    } else {
        echo "Le fichier n'a pas été uploadé (trop gros ?) ou ".
                "Le déplacement du fichier temporaire a échoué".
                " vérifiez l'existence du répertoire ".$repertoireDestination;
    }
}
?>
</body>
</html>
    
Cette fois nous avons utilisé la move_uploaded_file() et repris l'extension du fichier d'origine pour composer le nom de sauvegarde du fichier.
warning
  • Assurez vous qu'en aucun cas, votre script permettra la sauvegarde d'un fichier avec une extension .php (ou similaire). Cela autoriserait n'importe qui a déposer un script malvaillant sur votre serveur puis à l'exécuter pour effacer ou prendre les commandes de votre site web. Et pour commencer, ne construisez pas votre nom de fichier directement avec $_FILES['monfichier']['name'] sans contrôles préalables.
  • De même il est déconseillé de conserver le nom du fichier tel qu'il était sur le disque de l'utilisateur car il pourrait arriver que 2 utilisateurs transfèrent des fichiers différents sous le même nom et, à moins de spécifier des chemins différents par utilisateur, cela aurait pour effet d'écraser l'un des fichiers. Il est donc préférable de donner à ces fichiers un nom qui soit unique par utilisateur soit en créant des répertoires par utilisateur (ce qui peut faire un grand nombre de répertoires) ou plutôt en leur donnant comme nom l'identifiant unique de l'utilisateur (exemple son login) suivi éventuellement d'un descriptif (ex: jdupond_icone.gif, jdupond_photo.gif, shadok_icone.gif).
Et c'est tout... Mais rassurez-vous, vous avez le droit de compliquer tout cela.