Téléversement de fichiers
Dernière mise à jour le 21 novembre 2022
Le formulaire
Pour téléverser un fichier, vous avez besoin d'un formulaire utilisant la méthode POST, ce qui signifie que les données (contenu du fichier) seront transmises dans le corps de la requête HTTP et non dans l'URL comme ce serait le cas avec une requête GET.
Champ de type file
Ce formulaire doit contenir un champ d'entrée de type file
, ce qui fait apparaître une boîte de sélection de fichier :
<form action="traiter_fichier.php" method="post"> Fichier : <input name="fichier" size="35" type="file"> </form>
Sortie (essayez pour voir!) :
Encodage multipart/form-data
Pour que le fichier sélectionné soit téléchargé vers le serveur (téléversé), vous devez absolument spécifier le type de codage des données (enctype
) suivant : multipart/form-data
.
<form action="traiter_fichier.php" method="post" enctype="multipart/form-data"> Fichier : <input name="fichier" size="35" type="file"> </form>
L'attribut enctype
de la balise form
spécifie le type de codage utilisé par le navigateur pour envoyer les données au serveur (quand la valeur de l'attribut method
est post
).
Le tableau suivant indique les types de codage courants :
Type de codage | Description |
---|---|
application/x-www-form-urlencoded |
Les données de formulaire organisées sous la forme de paires nom-valeur. Tous les caractères spéciaux sont encodés. Par exemple, les espaces deviennent des Exemple : Nom: Jean Narrache Age: 33 devient : Nom=Jean+Narrache&Age=33 Il s'agit là du format de codage standard (valeur par défaut avec la méthode POST). |
multipart/form-data |
Les données de formulaire sont codées sous la forme d'un message comportant une partie distincte pour chacun des champs. Nécessaire en présence de champs d'entrée de type Exemple de formulaire : <form action="http://patoche.org/televerser.php" method="post" enctype="multipart/form-data"> <input type="text" name="texte" value="Le texte"> <input type="file" name="fichier1"> <input type="file" name="fichier2"> <button type="submit">Submit</button> </form> Requête générée : POST /televerser.php HTTP/1.1 Host: patoche.org User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:29.0) Gecko/20100101 Firefox/29.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive Content-Type: multipart/form-data; boundary=--LALALALA Content-Length: 5540 |
text/plain |
Les données de formulaire sont codées en texte brut, sans aucun contrôle ni caractères de mise en forme Rarement utilisé, sauf pour des fins de teste et de débogage. |
Maintenant rassurez-vous : vous n'avez pas besoin de maîtriser ces différents types d'encodages! Retenez seulement que pour le téléversement de fichiers vous devez utiliser multipart/form-data
.
MAX_FILE_SIZE
Un champ caché nommé MAX_FILE_SIZE
doit précéder le champ input de type file
.
Il spécifie la taille maximale en octets d'un fichier pouvant être téléchargé vers le script.
Attention : cette valeur peut être contournée et il est nécessaire de vérifier la taille du fichier transmis une fois celui-ci rendu sur le serveur.
<form action="traiter_fichier.php" method="post" enctype="multipart/form-data"> <input type="hidden" name="MAX_FILE_SIZE" value="50000"> Fichier : <input name="fichier" size="35" type="file"> </form>
Nom du fichier
Enfin, faites attention au nom de l'élément input
de type text
qui sert à saisir le nom, car c'est lui qui déterminera l'indice dans le tableau $_FILES
qui sera utilisé par le script sur le serveur (voir plus loin).
Version complète du formulaire :
<form action="traiter_fichier.php" method="post" enctype="multipart/form-data"> <input type="hidden" name="MAX_FILE_SIZE" value="50000"> Fichier : <input name="fichier" size="35" type="file"> <input type="submit" value="Envoyer le fichier"> </form>
Traitement des fichiers téléversés
À sa réception sur le serveur, un fichier téléversé est stocké sous un nom temporaire dans un répertoire spécifique (ex : "/tmp/phprEXcl3").
Les informations qui se rattachent aux fichiers téléversés sont stockées côté serveur dans un tableau à deux dimensions appelé $_FILES
.
Exemple :
// informations sur le fichier envoyé via le champ d'entrée "fichier" $_FILES['fichier']['name'] $_FILES['fichier']['type'] ... // informations sur le fichier envoyé via le champ d'entrée "foobar" $_FILES['foobar']['name'] ...
En supposant que le fichier est envoyé via un champ d'entrée appelé "fichier", l'élément $_FILES['fichier']['name']
contiendra par exemple le "vrai" nom du fichier.
L'ensemble des informations disponibles est représenté dans le tableau suivant :
Dans l'exemple suivant, on utilise la fonction is_uploaded_file() pour déterminer si le fichier spécifié est bel et bien un fichier téléversé.
Même si ce n'est pas obligatoire, c'était une pratique répandue dans les scripts que de s'assurer qu'un fichier qu'on déplace d'un répertoire "système" vers un répertoire public est bien un fichier téléversé. Cela date d'une époque où PHP était un langage globalement moins "sécuritaire" qu'aujourd'hui.
On s'entend pour dire que cela n'est plus nécessaire maintenant.
Le bout de code suivant affiche le contenu du fichier reçu, à condition qu'il s'agisse véritablement d'un fichier téléversé :
// si le fichier contient du HTML ou du texte if (($_FILES['fichier']['type'] == "text/html") || ($_FILES['fichier']['type'] == "text/plain")) { // s'il s'agit vraiment d'un fichier téléversé (plus nécessaire // mais encore utilisé par habitude) if (is_uploaded_file($_FILES['fichier']['tmp_name'])) { // on envoie le contenu dans la sortie standard (le navigateur) readfile($_FILES['fichier']['tmp_name']); } } else { echo "Le fichier ne peut être affiché"; }
Pour stocker le fichier dans un répertoire de l'application, il faut utiliser la fonction move_uploaded_file().
Remarque : cette fonction vérifie également qu'il s'agit bien d'un fichier téléversé et il est encore moins nécessaire d'utiliser ici la fonction is_uploaded_file().
Le bout de code suivant déplace le fichier téléchargé dans le répertoire "fichiers" de l'application :
// répertoire où l'application conserve les fichiers téléversés $rep = 'fichiers/'; // nouveau chemin du fichier à conserver $fich = $rep . basename($_FILES['fichier']['name']); if (move_uploaded_file($_FILES['fichier']['tmp_name'], $fich)) { echo "Le fichier est valide et a été déplacé dans " . $fich; } else { echo "Problème lors du déplacement"; }
Remarque : la fonction basename() extrait le nom du fichier à partir du nom complet (enlève le chemin si existant).
Pour débogage
Le code suivant affiche toutes les données disponibles sur le fichier téléversé. N'hésitez pas à l'utiliser pour déboguer.
<?php $nom = $_FILES['fichier']['name']; $taille = $_FILES['fichier']['size']; $temp = $_FILES['fichier']['tmp_name']; $type = $_FILES['fichier']['type']; $erreur = $_FILES['fichier']['error']; echo "<pre>"; echo "Nom d'origine : $nom\n"; echo "Taille : $taille octets\n"; echo "Emplacement temporaire : $temp\n"; echo "Type MIME : $type\n"; echo "Code d'erreur : $erreur\n"; echo "</pre>"; ?>
Remarque : le code d'erreur retourné n'est malheureusement pas toujours fiable.
Le code suivant affiche l'état du téléchargement :
<?php if ($erreur = $_FILES['fichier']['error']) { echo "Une erreur s'est produite : "; switch ($erreur) { case UPLOAD_ERR_INI_SIZE: echo "le fichier est plus gros que le maximum autorisé"; break; case UPLOAD_ERR_FORM_SIZE: echo "le fichier est plus gros qu'indiqué dans le formulaire"; break; case UPLOAD_ERR_PARTIAL: echo "le fichier n'a été que partiellement téléversé"; break; case UPLOAD_ERR_NO_FILE: echo "aucun fichier n'a été téléversé"; } } else { echo "Fichier correctement téléversé"; } ?>
Problèmes courants
Parmi les problèmes rencontrés souvent avec le téléversement, il y a d'abord les valeurs limites imposées par la configuration PHP.
Les valeurs qui peuvent affecter le téléversement, telles que définies dans le fichier php.ini sont :
upload_max_filesize
post_max_size
memory_limit
max_file_uploads
max_execution_time
max_input_time
On peut vérifier ces valeurs à l'aide de la fonction phpinfo().
Les valeurs typiques de ces constantes sont :
upload_max_filesize = 2M post_max_size = 8M memory_limit = 128M max_file_uploads = 20 max_execution_time = 30 max_input_time = 60
Note personnelle : par le passé j'ai dû jouer avec la valeur des trois premières constantes pour autoriser le téléversement de gros fichiers.
Consultez la documentation pour en savoir plus!
Un autre problème fréquent consiste à oublier d'ouvrir à tous les internautes le répertoire cible de la fonction move_uploaded_file().
Sous Linux, on y parvient avec la commande suivante :
$ sudo chgrp www-data nom_du_répertoire
Enfin, si notre distribution Linux utilise le module de sécurité SELinux, il faudra changer le contexte de ce répertoire :
sudo chcon -R -t httpd_sys_rw_content_t répertoire
Et voilà! Bon téléversement!