Comment uploader un fichier vers son site via http ?

Comme ça revient très souvent sur les forums voici un tutoriel extrait de la documentation de php. Celui-ci est basé sur l'exemple d'upload d'image gif d'un poids inférieur à 20ko et une taille inférieure ou égale à 100 x 100 pixel maximum !
Le fichier final sera copié dans le répertoire images_up qui se trouve à la racine du fichier de traitement du formulaire d'upload.

Le

Upload simple

Le formulaire :

<html>
 <head>
 <title>Upload de fichier</title>
 </head>
 <body>
 <form name="formu" action="traitement.php" method="post" enctype="multipart/form-data">
 On remarque qu'il faut ajouter entype="multipart/form-data" pour uploader un fichier
 Votre fichier <input type="file" name="fichier" accept="image/gif" /><br/>
 <input type="submit" value="Go !!!!" />
 </form>
 </body>
 </html>

Maintenant le traitement du formulaire. Dans ce tutoriel les codes sont écrits pour la configuration suivante :

  • Short tags = Off
  • Registers globals = Off

Dans traitement.php nous récupérons les informations de notre fichier dans un tableau super global $_FILES ! celui ci contient :

  • name : le nom de l'image sur l'ordinateur du client
  • tmp_name : le nom temporaire de l'image sur le serveur (depuis la racine du disque)
  • size : la taille en octets du fichier
  • type : le type mime du fichier sélectionné par le client
  • error : l'erreur rapportée par php lors de l'upload de l'image

Voici un exemple de traitement du formulaire précédent :

<?php
if (isset($_FILES['fichier']))
{
	//$_FILES existe on récupère les infos qui nous intéressent
	$fichier=$_FILES['fichier']['name'];//nom réel de l'image
	$size=$_FILES['fichier']['size']; //poids de l'image en octets
	$tmp=$_FILES['fichier']['tmp_name'];//nom temporaire de l'image (sur le serveur)
	$type=$_FILES['fichier']['type'];//type de l'image
	//On récupère la taille de l'image
	list($width,$height)=getimagesize($tmp);
	if (is_uploaded_file($tmp)) //permet de vérifier si le fichier a été uplodé via http
	{
		//vérification du type de l'img, son poids et sa taille
		if ($type=="image/gif" && $size<=20500 && $width<=100 && $height<=100 )
		{
			// type mime gif, poids < à 20500 octets soit environ 20Ko, largeur = hauteur = 100px
			//Pour supprimer les espaces dans les noms de fichiers car celà entraîne une erreur lorsque vous voulez l'afficher
			$fichier = preg_replace ("` `i","",$fichier);//ligne facultative :)
			//On vérifie s'il existe une image qui a le même nom dans le répertoire
			if (file_exists('./images_up/'.$fichier))
			{
				//Le fichier existe on rajoute dans son nom le timestamp du moment pour le différencier de la première (comme cela on est sûr de ne pas avoir 2 images avec le même nom :) )
				$nom_final= preg_replace("`.gif`is",date("U").".gif",$fichier);
			}
			else {
				$nom_final=$fichier; //l'image n'existe pas on garde le même nom
			}
			//on déplace l'image dans le répertoire final
			move_uploaded_file($tmp,'./images_up/'.$nom_final);
			//Message indiquant que tout s'est bien passé
			echo "L'image a été uploadée avec succès<br/>";
		}
		else {
			//Le type mime, ou la taille ou le poids est incorrect
			echo 'Votre image a été rejetée (poids, taille ou type incorrect)';
		}
	}
}
//Pour tester si l'image est bien à sa place
echo '<img src="./images_up/'.$nom_final.'" border="0" />';
echo '<br/>';
echo '<a href="javascript:history.back();">Retour</a>';
?>

Upload multiple:

Pour l'upload multiple il `suffit` d'utiliser la notation tableau (fichier) dans le nom des balises input de type file Le formulaire devient donc :

<html>
 <head>
 <title>Upload de fichier</title>
 </head>
 <body>
 <form name="formu" action="traitement.php" method="post" enctype="multipart/form-data">
 On remarque qu'il faut ajouter entype="multipart/form-data" pour uploader un fichier
 fichier 1 <input type="file" name="fichier[]" accept="image/gif" /><br />
 fichier 2 <input type="file" name="fichier[]" accept="image/gif" /><br />
 fichier 3 <input type="file" name="fichier[]" accept="image/gif" /><br />
 fichier 4 <input type="file" name="fichie[]" accept="image/gif" /><br />
 fichier 5 <input type="submit" value="Go !!!!" />
 </form>
 </body>
 </html>

Pour le traitement, on utilisera une boucle foreach dans laquelle on vérifiera les informations relatives à nos fichiers et on validera ou non ces fichiers !

<?php
if (isset($_FILES['fichier']))
{
	//$_FILES existe
	foreach ($_FILES['fichier']['name'] as $key => $value) {
		$fichier=$value;//nom reel de l'image
		$size=$_FILES['fichier']['size'][$key]; //poids de l'image en octets
		$tmp=$_FILES['fichier']['tmp_name'][$key];//nom temporaire de l'image (sur le serveur)
		$type=$_FILES['fichier']['type'][$key];//type de l'image
		//On récupère la taille de l'image
		list($width,$height)=getimagesize($tmp);
		if (is_uploaded_file($tmp)) //permet de vérifier si le fichier a été uplodé via http
		{
			//vérification du type de l'img, son poids et sa taille
			if ($type=="image/gif" && $size<=20500 && $width<=100 && $height<=100 )
			{
				// type mime gif, poids < à 20500 octets soit environ 20Ko, largeur = hauteur = 100px
				//Pour supprimer les espaces dans les noms de fichiers car celà entraîne une erreur lorsque vous voulez l'afficher
				$fichier = preg_replace ("` `i","",$fichier);//ligne facultative :)
				//On vérifie s'il existe une image qui a le même nom dans le répertoire
				if (file_exists('./images_up/'.$fichier))
				{
					//Le fichier existe on rajoute dans son nom le timestamp du moment pour le différencier de la première (comme cela on est sûr de ne pas avoir 2 images avec le même nom :) )
					$nom_final= preg_replace("`.gif`is",date("U").".gif",$fichier);
				}
				else {
					$nom_final=$fichier; //l'image n'existe pas on garde le même nom
				}
				//on déplace l'image dans le répertoire final
				if (move_uploaded_file($tmp,'./images_up/'.$nom_final)) {
					//Message indiquant que tout s'est bien passé
					//Pour le test on affiche l'image
					echo '<img src="./images_up/'.$nom_final.'" border="0" />';
					echo '<br/>';
				}
				else {
					//l'image ne peut être déplacée, on supprime le fichier temporaire
					@ unlink ($tmp);
					echo 'L'image n'a pu être déplacée !';
				}

			}
			else {
				//Le type mime, ou la taille ou le poids est incorrect
				echo 'Votre image a été rejetée (poids, taille ou type incorrect)';
			}
		}
	}
}
echo '<a href="javascript:history.back();">Retour</a>';
?>

Gestion des erreurs d'upload

Les erreurs qui se produisent pendant l'upload sont renseignées par le champ error du tableau $_FILES Les différentes erreurs :

  • 0 : pas d'erreur
  • 1 : le fichier dépasse la taille maximale d'upload autorisée par le webmestre (avec le champ caché MAXFILESIZE)
  • 2 : Votre fichier dépasse la taille maximale demandée par le Webmestre
  • 3 : fichier pas totalement uploadé sur le serveur
  • 4 : aucun fichier téléchargé

Pour effectuer la gestion des erreurs, une petite fonction (afin d'alléger un peu le code)

<?php
function up_error($code,$nom) {
	switch ($code) {
		case UPLOAD_ERR_OK : $erreur = 'Pas d'erreur';$valid = true;break;
		case UPLOAD_ERR_INI_SIZE : $erreur = 'Votre fichier `'.$nom.'` dépasse la taille maximale d'upload autorisée par PHP( '.get_cfg_var('upload_max_filesize').' )';$valid = false;break;
		case UPLOAD_ERR_FORM_SIZE : $erreur = 'Votre fichier dépasse la taille maximale demandée par le Webmestre';$valid = false;break;
		case UPLOAD_ERR_PARTIAL : $erreur = 'Le fichier n'a été que partiellement téléchargé. !!!';$valid = false;break;
		case UPLOAD_ERR_NO_FILE : $erreur = 'Aucun fichier téléchargé !!!';$valid = false;break;
		case UPLOAD_ERR_NO_TMP_DIR : $erreur = 'Un dossier temporaire est manquant.'
		case UPLOAD_ERR_CANT_WRITE : $erreur = 'Échec de l'écriture du fichier sur le disque.'
		case UPLOAD_ERR_EXTENSION : $erreur = 'Une extension PHP a arrété l'envoi de fichier. PHP ne propose aucun moyen de déterminer quelle extension est en cause. L'examen du phpinfo() peut aider.';
		default : $erreur = 'L'upload a rencontré une erreur inconnue !!!';$valid = false; break;
	}
    
	$return[] = $valid;
	$return[] = $erreur;
	return $return;
}
?>

Le 1er script avec la gestion d'erreurs :

<?php
if (isset($_FILES['fichier']))
{
	//$_FILES existe on récupère les infos qui nous intéressent
	$fichier=$_FILES['fichier']['name'];//nom réel de l'image
	$size=$_FILES['fichier']['size']; //poid de l'image en octets
	$tmp=$_FILES['fichier']['tmp_name'];//nom temporaire de l'image (sur le serveur)
	$type=$_FILES['fichier']['type'];//type de l'image
	$error = $_FILES['fichier']['error'];
	$retour = up_error($error,$fichier);
	if ($retour[0] === true) {
		//On récupère la taille de l'image
		list($width,$height)=getimagesize($tmp);
		if (is_uploaded_file($tmp)) //permet de vérifier si le fichier a été uploadé via http
		{
			//vérification du type de l'img, son poids et sa taille
			if ($type=="image/gif" && $size<="20500" && $width<="100" && $height<="100" )
			{
				// type mime gif, poids < à 20500 octets soit environ 20Ko, largeur = hauteur = 100px
				//Pour supprimer les espaces dans les noms de fichiers car celà entraîne une erreur lorsque vous voulez l'afficher
				$fichier = preg_replace ("` `i","",$fichier);//ligne facultative :)
				//On vérifie s'il existe une image qui a le même nom dans le répertoire
				if (file_exists('./images_up/'.$fichier))
				{
					//Le fichier existe on rajoute dans son nom le timestamp du moment pour le différencier de la première (comme cela on est sur de ne pas avoir 2 images avec le même nom :) )
					$nom_final= preg_replace("`.gif`is",date("U").".gif",$fichier);
				}
				else {
					$nom_final=$fichier; //l'image n'existe pas on garde le même nom
				}
				//on déplace l'image dans le répertoire final
				move_uploaded_file($tmp,'./images_up/'.$nom_final);
				//Message indiquant que tout s'est bien passé
				echo "L'image a été uploadée avec succès<br/>";
			}
			else {
				//Le type mime, ou la taille ou le poids est incorrect
				echo 'Votre image a été rejetée (poids, taille ou type incorrect)';
			}
		}
		else {
			echo $retour[1],'<br />';
		}
	}
}
//Pour tester si l'image est bien à sa place
echo '<img src="./images_up/'.$nom_final.'" border="0" />';
echo '<br/>';
echo '<a href="javascript:history.back();">Retour</a>';
?>

Conclusion

Voilà avec tout cela vous avez de quoi faire un gestionnaire d'upload, sans trop de problèmes. Rappelez-vous de toujours vérifier les informations fournies par l'utilisateur ( $_GET, $_POST, $_COOKIE, $_FILES ....) afin d'éviter au maximum les trous de sécurité.
Pour compléter vous pouvez consulter cette liste de types mime
Ou celle-ci
Ceci n'est pas forcément exempt d'erreur (oublie bête) ils vous est extrêmement conseillé de lire et comprendre le code avant de l'utiliser !

Merci à rafaelle pour son aimable relecture et correction d'orthographe :)