Chargement de fichier par la méthode POST
Le chargement de fichier tel que nous le connaissons le plus couramment à été introduit en novembre 1995 par la RFC-1867. Si vous souhaitez plus de détails sur le fonctionnement de cette méthode je vois renvois directement vers la RFC. Dans notre article nous ne verrons que comment mettre tout cela en œuvre au sein d'un l'environnement AMP.
Nous réaliserons l'étude de cas suivante à titre d'exemple :
A souhaite pouvoir charger des fichiers dans le répertoire /upload/ de son site internet via un simple formulaire disponible depuis son navigateur.
Nous utiliserons l'arborescence de la figure 1-1. Le répertoire /upload/ contiendra les fichiers chargés par le visiteur, le fichier /upload.php contiendra le formulaire ainsi que le script PHP permettant le chargement des images. Quand au fichier /.htaccess il pourra nous permettre de modifier certaines valeurs par défaut de PHP un peu gênante que nous verrons plus bas.
Figure 1-1 : Arborescence
Le formulaire HTML
Comme nous l'avons abordé plus haut, l'envoi de fichier via la méthode POST est décrit par la RFC-1867. Comme exposé au sein de la RFC, côté HTML nous devons résoudre 3 impératifs :
- que notre formulaire utilise la méthode POST
- que l'attribut « enctype » de notre balise « form » prenne la valeur « multipart/form-data »
- que le champ accueillant le fichier à envoyer soit de type « file »
Pour résoudre proprement notre étude de cas, nous ajouterons aussi un petit message de retour, pour indiquer si le chargement c'est bien passé, s'il a échoué … Ce message sera géré en PHP et stocké dans la variable $information.
Voici donc une première partie de notre fichier upload.php :
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8">
<title>Chargement de Fichiers</title>
</head>
<body>
<form method="post" enctype="multipart/form-data">
<p><?php echo $information; ?></p>
<p>
<label for="fichier">Fichier à envoyer :</label>
<input type="file" id="fichier" name="fichier" />
</p>
<p>
<input type="submit" value="Envoyer" />
</p>
</form>
</body>
</html>
Notre formulaire sera donc très simple et ressemblera à quelque chose comme le montre la figure 1-2. Remarquez que le navigateur s'occupe lui-même de proposer un champ parcourable au visiteur pour que celui-ci puisse sélectionner le fichier de son choix.
Figure 1-2 : Le formulaire
Traiter le formulaire en PHP
Maintenant que nous avons rédigé notre formulaire ; nous allons voir comment en lire les données une fois celui-ci envoyé. Si les données traditionnelles envoyées depuis des formulaires avec la méthode POST sont lues la superglobale $_POST ; les choses sont un peu différentes concernant les champs de type file
lors de la validation du formulaire
Lors de la validation du formulaire, le client va envoyer le fichier à l'aide de son navigateur. PHP et apache viendrons alors placer ce fichier dans un répertoire temporaire. C'est l'étape 1 que vous pouvez retrouver sur la figure 2-1.
Ce fichier sera conservé à cet endroit durant toute l'exécution du script uniquement. Une fois le script achevé et pour des raisons de sécurités évidentes le fichier sera supprimé de cet emplacement.
Vous pouvez connaître le nom du répertoire utilisé comme répertoire temporaire via la directive upload_tmp_dir du php.ini. Généralement il s'agit par défaut de /tmp/.
Figure 2-1 : Déplacements du fichier
Comprenez bien que vous devez donc, déplacer le fichier et l'enregistrer ailleurs si vous souhaitez conserver celui-ci après la fin de l'exécution du script. C'est ce que nous allons écrire en PHP.
Dans notre étude de cas, nous voulons stocker nos fichiers dans le répertoire /upload/ ; nous devrons donc déplacer notre fichier du répertoire temporaire pour le placer dans le répertoire /upload/. C'est la deuxième étape représentée sur la figure 2-1.
la super globale $_FILES
Pour lire un les données associés aux champs de type file depuis un script PHP, vous devrez utiliser la superglobale $_FILES. Il s'agit d'un tableau multidimensionnel contenant à son premier niveau, un indice par fichier envoyé via un champ de formulaire de type file. Le nom de l'indice correspond a la valeur de l'attribut « name » de votre formulaire.
Si nous reprenons notre étude de cas, la superglobale $_FILES contiendra donc un indice nommé « fichier ». $_FILES['fichier'] sera donc égal à un tableau contenant un ensemble de valeurs qui nous informera sur le fichier concerné.
Le tableau que vous trouverez dans $_FILES['fichier'], ou dans $_FILES[{n'importe quel fichier}] contiendra les valeurs suivantes :
name Le nom original du fichier, tel que sur la machine du client web.
type Le type MIME du fichier, si le navigateur a fourni cette information. Par exemple, cela pourra être "image/gif". Ce type mime n'est cependant pas vérifié du côté de PHP et, donc, ne prend pas sa valeur pour se synchroniser.
size La taille, en octets, du fichier téléchargé.
tmp_name Le nom temporaire du fichier qui sera chargé sur la machine serveur
error Le code d'erreur associé au téléchargement de fichier. Cet élément a été introduit en PHP 4.2.0
Pour information, sachez que $_FILES n'est utilisé que depuis PHP4.1, avant il fallait utiliser $HTTP_POST_FILES mais considérez ce dernier comme obsolète.
les différentes erreurs
Comme vous venez de le voir, le tableau $_FILES['fichier'] contient un indice « error » qui permet de connaître le statut du téléchargement du fichier associé.
Voici un tableau récapitulatif des valeurs possible que vous pourrez rencontrer :
UPLOAD_ERR_OK 0 Aucune erreur, le téléchargement est correct
UPLOAD_ERR_INI_SIZE 1 Le fichier téléchargé excède la taille de upload_max_filesize, configurée dans le php.ini
UPLOAD_ERR_FORM_SIZE 2 Le fichier téléchargé excède la taille de MAX_FILE_SIZE, qui a été spécifiée dans le formulaire HTML.
UPLOAD_ERR_PARTIAL 3 Le fichier n'a été que partiellement téléchargé
UPLOAD_ERR_NO_FILE 4 Aucun fichier n'a été téléchargé
UPLOAD_ERR_NO_TMP_DIR 6 Un dossier temporaire est manquant. Introduit en PHP 4.3.10 et PHP 5.0.3
UPLOAD_ERR_CANT_WRITE 7 Échec de l'écriture du fichier sur le disque. Introduit en PHP 5.1.0
UPLOAD_ERR_EXTENSION 8 L'envoi de fichier est arrêté par l'extension. Introduit en PHP 5.2.0
le code PHP
Maintenant que nous avons vu ensemble l'essentiel sur ce que vous devait savoir, je vais vous proposer la suite et fin de notre petit exemple : la partie concernant les traitements en PHP. Vous pourrez également trouver l'ensemble des sources concernant notre étude de cas en annexe à ce tutoriel.
Notre traitement en PHP va se diviser en 3 actions simples à réaliser :
- vérifier que le formulaire a bien été envoyé
- vérifier que le fichier à bien été chargé dans le répertoire temporaire
- déplacer le fichier vers le répertoire /upload/
Bien évidement, comme nous l'avons vu lors de la création du formulaire plus haut, tout au long de ces traitements nous prendrons soin en cas d'erreur d'en avertir l'utilisateur final à l'aide de la variable $information.
$upload_path = 'upload/';
if (isset($_FILES['fichier'])) {
if ($_FILES['fichier']['error']===UPLOAD_ERR_OK) {
$filename = $upload_path.$_FILES['fichier']['name'];
if (move_uploaded_file($_FILES['fichier']['tmp_name'], $filename)) {
$information = 'Le fichier à bien été enregistré';
} else {
$information = 'Impossible d\'enregistrer le fichier';
}
} else {
$information = 'Impossible de charger le fichier. Code d\'erreur #'.$_FILES['fichier']['error'];
}
} else {
$information = 'Selectionner le fichier a envoyer';
}
Annexe
informations à connaître
Allez donc faire un tour sur la documentation PHP, et regardez les choses suivantes concernant le chargement de fichier par la méthode POST :
Directives dans le php.ini :
- file_uploads
- upload_max_filesize
- upload_tmp_dir
- post_max_size
- max_input_time
Fonctions à connaître :
- is_uploaded_file()
- move_uploaded_file()
Une piste si vous souhaitez implémenter une barre de progression : apc.rfc1867
upload_max_filesize
PHP a besoin d'une limite de taille pour les fichiers envoyés via cette méthode. Généralement dans la plupart des configuration par défaut cette limite peut parfois être trop basse, empêchant ainsi vos utilisateurs de charger des fichiers un minimum conséquents (comme un gros fichier word, ou une image haute définition par exemple).
En théorie ce genre de problème est forcé de vous arriver au moins une fois pendant votre vie. Sachez que vous pouvez vous-même modifier la taille maximum des fichiers via la directive PHP upload_max_filesize.
Pour modifier cette valeur, vous pouvez par exemple la changer depuis un fichier .htaccess placé dans le répertoire contenant votre script PHP. Ajouter simplement les lignes suivantes dans le fichier (xxxxx étant la nouvelle valeur que vous souhaitez attribuer. Il s'agit du nombre d'octet maximum que peut représenter un fichier chargé) :
php_value upload_max_filesize xxxxx
Conclusion
Comme nous avons pu le voir, PHP implémente de façon plus que satisfaisante la RFC-1867 ce qui nous permet, couplé avec du HTML, de proposer aux utilisateurs de nos applications un moyen de pouvoir saisir des fichiers.
Le tout malgré la faiblesse de HTML 4 qui n'implémente aucun support de la méthode HTTP PUT dont le rôle est dédié à la création de nouvelles ressources.
Cependant le support de la méthode PUT est à l'ordre du jour si l'on en crois le brouillon de HTML 5 publié par le W3C. Dans un futur proche, nous pourrions donc implémenté des formulaire avec la méthode PUT au sein de nos pages web sans avoir à ajouter de sur-couche en JavaScript comme c'est le cas actuellement.