Uploader des images sur un serveur web
- Par Emacs
- 31 commentaires
- 31 489 lectures
- Format PDF
- RSS - Atom
Beaucoup d'applications web ont recours à des modules d'upload d'images. Parmi elles nous pouvons citer les galleries d'images ou bien encore les systèmes d'avatars de forums type PHPBB, VBulletin ou IPB (pour ne citer que les plus connus). Les programmes permettant ce genre de fonctionnalités peuvent-être parfois très évolués car ils se chargent de contrôler l'intégrité du fichier ou bien même de le redimensionner à la volée.
Le script qui suit montre le fonctionnement d'un système d'upload d'images sur le serveur Web. Ses principales caractéristiques sont les suivantes :
- Il est entièrement configurable grâce aux constantes situées en tête du script.
- Il dispose d'un tableau d'extensions d'image autorisées. Les extensions possibles sont celles acceptées par la fonction getimagesize() de PHP.
- Il crée le répertoire cible s'il n'existe pas.
- Il contrôle l'extension du fichier envoyé.
- Il contrôle le type de l'image.
- Il contrôle les dimensions de l'image (largeur et hauteur).
- Il contrôle le poids de l'image.
- Il upload l'image sur le serveur avec un nouveau nom.
- Il retourne un message de réussite ou d'erreur à l'utilisateur.
- Il est sécurisé.
Le script complet
Script PHP de l'upload d'image<?php/************************************************************* Script realise par Emacs* Crée le 19/12/2004* Maj : 23/06/2008* Licence GNU / GPL* webmaster@apprendre-php.com* http://www.apprendre-php.com* http://www.hugohamon.com** Changelog:** 2008-06-24 : suppression d'une boucle foreach() inutile* qui posait problème. Merci à Clément Robert pour ce bug.**************************************************************//************************************************************* Definition des constantes / tableaux et variables*************************************************************/// Constantes// Tableaux de donnees// Variables$extension = '';$message = '';$nomImage = '';/************************************************************* Creation du repertoire cible si inexistant*************************************************************/exit('Erreur : le répertoire cible ne peut-être créé ! Vérifiez que vous diposiez des droits suffisants pour le faire ou créez le manuellement !');}}/************************************************************* Script d'upload*************************************************************/{// On verifie si le champ est rempli{// Recuperation de l'extension du fichier// On verifie l'extension du fichier{// On recupere les dimensions du fichier// On verifie le type de l'imageif($infosImg[2] >= 1 && $infosImg[2] <= 14){// On verifie les dimensions et taille de l'imageif(($infosImg[0] <= WIDTH_MAX) && ($infosImg[1] <= HEIGHT_MAX) && (filesize($_FILES['fichier']['tmp_name']) <= MAX_SIZE)){// Parcours du tableau d'erreurs&& UPLOAD_ERR_OK === $_FILES['fichier']['error']){// On renomme le fichier// Si c'est OK, on teste l'upload{$message = 'Upload réussi !';}else{// Sinon on affiche une erreur systeme$message = 'Problème lors de l\'upload !';}}else{$message = 'Une erreur interne a empêché l\'uplaod de l\'image';}}else{// Sinon erreur sur les dimensions et taille de l'image$message = 'Erreur dans les dimensions de l\'image !';}}else{// Sinon erreur sur le type de l'image$message = 'Le fichier à uploader n\'est pas une image !';}}else{// Sinon on affiche une erreur pour l'extension$message = 'L\'extension du fichier est incorrecte !';}}else{// Sinon on affiche une erreur pour le champ vide$message = 'Veuillez remplir le formulaire svp !';}}?>
Le formulaire HTML<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr"><head><title>Upload d'une image sur le serveur !</title></head><body><?phpif( !empty($message) ){echo '<p>',"\n";echo "\t\t<strong>", htmlspecialchars($message) ,"</strong>\n";echo "\t</p>\n\n";}?><!-- Debut du formulaire --><form enctype="multipart/form-data" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" method="post"><fieldset><legend>Formulaire</legend><p><label for="fichier_a_uploader" title="Recherchez le fichier à uploader !">Envoyer le fichier :</label><input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_SIZE; ?>" /><input name="fichier" type="file" id="fichier_a_uploader" /><input type="submit" name="submit" value="Uploader" /></p></fieldset></form><!-- Fin du formulaire --></body></html>
Les commentaires
2. Par Emacs le mercredi 05 décembre 2007 à 13:01
Bonjour,
Qu'appellez-vous "extensions fausses" ? La fonction pathinfo() renvoie la véritable extension du fichier envoyé.
Cordialement.
3. Par Bruno le mercredi 16 janvier 2008 à 00:12
Bonjour .
Bravo pour votre site très soigné .
Bon tuto , mais il manque la protection contre les insertions de code dans la source mème de l'image , seul readfile peut contrer cette ultime attaque .
Bruno
4. Par Emacs le mercredi 16 janvier 2008 à 09:09
Effectivement Bruno ! Je corrige ça dans l'après midi
![]()
5. Par Emacs le mercredi 16 janvier 2008 à 20:08
Correction apportée. L'image est renommée à la volée avec une chaine MD5 aléatoire générée par un uniqid().
6. Par Bruno le jeudi 17 janvier 2008 à 08:08
Oui , en fait , mon post n'a pas un rapport direct avec ton tutoriel .
Il intervient seulement si l'on doit afficher des images du client et dont on ignore l'origine .
http://www.nexen.net/actualites/securite/17226-attaque_par_image_gif.php
Bruno
7. Par Emacs le jeudi 17 janvier 2008 à 09:09
Oui je suis d'accord avec toi. Sachant qu'en plus j'ai lu récemment le livre de Damien Séguy. Il parle de la sécurité des noms de fichiers. On peut effectivement avoir des attaques XSS avec un nom de fichier malsaint.
8. Par Emacs le vendredi 18 janvier 2008 à 00:12
Oui c'est exact avec IE 6. Il faut ouvrir le fichier gif et contrôler qu'il n'y ait pas de code PHP à l'intérieur.
9. Par Mazzu le dimanche 20 janvier 2008 à 02:02
J'ai fait très récemment un script d'upload qui ressemble beaucoup à ça. C'est marrant
Je vais me permettre quelques petites remarques.
- En rapport avec la remarque de Bruno, il faut ou bien faire comme phpclasses.org l'explique ou bien s'assurer que le fichier n'a pas une extension de fichier qui soit interprété par PHP (comme c'est fait ici) ou bien placer les images dans un répertoire à part qui contient un .htaccess avec la ligne : "php_flag engine Off" et s'assurer de ne pas écraser le .htaccess
- Ensuite, juste pour avoir quelque chose de plus propre, j'aurais changé les lignes suivantes,
ligne 49 : if(!empty($_FILES['fichier']['name']) && $_FILES['fichier']['error'] === UPLOAD_ERR_OK)
ligne 58 : $infosImg = @getimagesize($_FILES['fichier']['tmp_name']);
ligne 61 : if($infosImg !== FALSE && $infosImg[2] >= 1 && $infosImg[2] <= 14)
10. Par Emacs le dimanche 20 janvier 2008 à 10:10
Bonjour Mazzu,
Ta solution du .htaccess est pas mal mais ce n'est pas la meilleure. Pour sécuriser encore un peu mieux, il faudrait placer les fichiers uploadés dans un répertoire "hors web". Néanmoins la création de répertoires au dessus de la racine web est très rarement autorisée sur des serveurs mutualisés.
Quant à la constante UPLOAD_ERR_OK, tu viens de me l'apprendreJe vais l'intégrer au script je pense.
Pour la ligne 58, le @ ne sert strictement rien si le serveur est bien configuré pour masquer les erreurs en production.
A la ligne 61, effectivement je n'ai pas vérifié la valeur de retour de la variable $infosImg.
Je te remercie pour ton commentaire constructif Mazzu
Hugo.
11. Par Mazzu le dimanche 20 janvier 2008 à 18:06
You're Welcome !
Pour la constante UPLOAD_ERR_OK, je n'ai rien inventé, tout est là
http://fr3.php.net/manual/fr/features.file-upload.php
12. Par bruno le mardi 22 janvier 2008 à 15:03
Bonjour a vous .
Finalement , la meilleure solution serait d'utiliser le format png a l'instar du gif . Avec le patch png pour IE .
Ce n'est qu'une idée .
Bruno
13. Par rocawear le mardi 29 janvier 2008 à 03:03
Bonjour a vous tous,
j'ai deja subit une attaque dans le passé car les fichier de type .php.ext fonctionais et php les executais.
pour resoudre ce probleme j'ai du faire une cimple condition toute bête !
Voila:
if(preg_match('#php#isU',$fichier)){ // Si le fichier comporte .php.jext, ont renome le php en 484 ( a vous de choisir )
$fichier = str_replace('php','484',$fichier);
}
14. Par Raherivo le vendredi 14 mars 2008 à 13:01
Bonjour à tous,
Je demanderai à vous, donnez moi le code du modification d'upload image s'il vous plaît! je n'en trouvais plus.
Merci beaucoup à bientôt!
15. Par Jeux le mercredi 02 juillet 2008 à 09:09
Merci ce script me sera bien utile, surtout avec toutes les modifications apportées petit à petit
16. Par pasilage le lundi 20 octobre 2008 à 14:02
bonjour excuser moi je decouvre le php, doit on mettre le formulaire et php dans le même fichier ou deux fichier distin? Si il sont distin comment les lier le action du formulaire ou habituellement on mais le nom du fichier et bizare je ne comprend pas
17. Par Emacs le lundi 20 octobre 2008 à 22:10
Dans le même c'est mieux pour pouvoir gérer les erreurs.
18. Par pasilage le mardi 21 octobre 2008 à 01:01
ou doije mettre le nom du fichier de destination je l'ai mis a la place de files mais ca ne marche pas il charge une page quand j'execute le script mais la page reste blanche et pas d'image dans mon dossier
19. Par pasilage le mardi 21 octobre 2008 à 01:01
Constantesdefine
('TARGET', 'C:/Program Files/EasyPHP 2.0b1/www/'); DOIT JE PARAMETRER AUTRE CHOSE MERCI DE M AIDER
20. Par pasilage le mardi 21 octobre 2008 à 10:10
j'ai enfin reussit a faire fonctionner le script une erreur de frappe de ma part il marche bien c'est pile ce que je voulais merci
21. Par pasilage le mercredi 22 octobre 2008 à 11:11
bonjour j'ai ajoutés un script de redimentionnement bon la forme n'est peut être pas top car je débute mais ça marche dit moi ce que tu en pense
<?php
define('TARGET', 'C:\Program Files\EasyPHP 2.0b1\www\aaaa\image');
define('MAX_SIZE', 200000);
define('WIDTH_MAX', 800);
define('HEIGHT_MAX', 800);
$tabExt = array('jpg','gif','png','jpeg');
$infosImg = array();
$extension = '';
$message = '';
$nomImage = '';
if( !is_dir(TARGET) ) {
if( !mkdir(TARGET, 0755) ) {
exit('Erreur : le répertoire cible ne peut-être créé ! Vérifiez que vous diposiez des droits suffisants pour le faire ou créez le manuellement !');
}
}
if(!empty($_POST))
{
// On verifie si le champ est rempli
if( !empty($_FILES['fichier']['name']) )
{
// Recuperation de l'extension du fichier
$extension = pathinfo($_FILES['fichier']['name'], PATHINFO_EXTENSION);
// On verifie l'extension du fichier
if(in_array(strtolower($extension),$tabExt))
{
// On recupere les dimensions du fichier
$infosImg = getimagesize($_FILES['fichier']['tmp_name']);
// On verifie le type de l'image
if($infosImg[2] >= 1 && $infosImg[2] <= 14)
{
// On verifie les dimensions et taille de l'image
if(($infosImg[0] <= WIDTH_MAX) && ($infosImg[1] <= HEIGHT_MAX) && (filesize($_FILES['fichier']['tmp_name']) <= MAX_SIZE))
//on redimentionne limage
$x = 75;
$y = 75; # Taille en pixel de l'image redimensionnée
if ($infosImg)
echo 'Image redimensionnée ...
';
if ($infosImg['mime']=='image/jpeg' ) {
$img_big = imagecreatefromjpeg($_FILES['fichier']['tmp_name']); # On ouvre l'image d'origine
$img_new = imagecreate($x, $y);
# création de la miniature
$img_mini = imagecreatetruecolor($x, $y)
or $img_mini = imagecreate($x, $y);
// copie de l'image, avec le redimensionnement.
imagecopyresized($img_mini,$img_big,0,0,0,0,$x,$y,$infosImg[0],$infosImg[1]);
imagejpeg($img_mini,$_FILES['fichier']['tmp_name'] );
}
elseif ($infosImg['mime']=='image/png' ) {
$img_big = imagecreatefrompng($_FILES['fichier']['tmp_name']); # On ouvre l'image d'origine
$img_new = imagecreate($x, $y);
# création de la miniature
$img_mini = imagecreatetruecolor($x, $y)
or $img_mini = imagecreate($x, $y);
// copie de l'image, avec le redimensionnement.
imagecopyresized($img_mini,$img_big,0,0,0,0,$x,$y,$infosImg[0],$infosImg[1]);
imagepng($img_mini,$_FILES['fichier']['tmp_name'] );
}
elseif ($infosImg['mime']=='image/gif' ) {
$img_big = imagecreatefromgif($_FILES['fichier']['tmp_name']); # On ouvre l'image d'origine
$img_new = imagecreate($x, $y);
# création de la miniature
$img_mini = imagecreatetruecolor($x, $y)
or $img_mini = imagecreate($x, $y);
// copie de l'image, avec le redimensionnement.
imagecopyresized($img_mini,$img_big,0,0,0,0,$x,$y,$infosImg[0],$infosImg[1]);
imagegif($img_mini,$_FILES['fichier']['tmp_name'] );
}
{
// Parcours du tableau d'erreurs
if(isset($_FILES['fichier']['error'])
&& UPLOAD_ERR_OK === $_FILES['fichier']['error'])
{
// On renomme le fichier
$nomImage = md5(uniqid()) .'.'. $extension;
// Si c'est OK, on teste l'upload
if(move_uploaded_file($_FILES['fichier']['tmp_name'], TARGET.$nomImage))
{
$message = 'Upload réussi !';
}
else
{
// Sinon on affiche une erreur systeme
$message = 'Problème lors de l\'upload !';
}
}
else
{
$message = 'Une erreur interne a empêché l\'uplaod de l\'image';
}
}
}
else
{
// Sinon erreur sur les dimensions et taille de l'image
$message = 'Erreur dans les dimensions de l\'image !';
}
}
else
{
// Sinon erreur sur le type de l'image
$message = 'Le fichier à uploader n\'est pas une image !';
}
}
else
{
// Sinon on affiche une erreur pour l'extension
$message = 'L\'extension du fichier est incorrecte !';
}
}
else
{
// Sinon on affiche une erreur pour le champ vide
$message = 'Veuillez remplir le formulaire svp !';
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr"> <head> <title>Upload d'une image sur le serveur !</title> </head> <body> <?php if( !empty($message) ) { echo '<p>',"\n"; echo "\t\t<strong>", htmlspecialchars($message) ,"</strong>\n"; echo "\t</p>\n\n"; } ?> <!-- Debut du formulaire --> <form enctype="multipart/form-data" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" method="post"> <fieldset> <legend>Formulaire</legend> <p> <label for="fichier_a_uploader" title="Recherchez le fichier à uploader !">Envoyer le fichier :</label> <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_SIZE; ?>" /> <input name="fichier" type="file" id="fichier_a_uploader" /> <input type="submit" name="submit" value="Uploader" /> </p> </fieldset> </form> <!-- Fin du formulaire --> </body></html>
22. Par Emacs le jeudi 23 octobre 2008 à 00:12
Bonjour,
Peux-tu déplacer ce commentaire dans les forums stp. Je supprimerai ton message dans les heures qui viennent car il n'a pas sa place dans les commentaires.
++
Hugo.
23. Par Uzumaki le vendredi 24 octobre 2008 à 02:02
bonjour je voudrai savoir si le script du formulaire et celui du upload se mette sur la même page je voudrais savoir comment procéder merci ( au passage je suis un débutant)
24. Par phpmen le samedi 20 décembre 2008 à 13:01
merci
![]()
25. Par didi le vendredi 17 avril 2009 à 04:04
jai un petit probleme avec le script de pasilage getimagesize() [function.getimagesize]: Filename cannot be empty in
26. Par xsaiddx le lundi 18 mai 2009 à 16:04
bonjour j'ai pas compris cette ligne
$infosImg = getimagesize($_FILES['fichier']['tmp_name']);
// On verifie le type de l'image
if($infosImg[2] >= 1 && $infosImg[2] <= 14)
et sur la doc j'ai trouve ca ==>
L'index 2 est une constante parmi IMAGETYPE_XXX, indiquant le type de l'image.
alors je comprend encore pas qu'est ce qu'il ya dans l'index 2
merci
27. Par Emacs le lundi 18 mai 2009 à 19:07
La doc de la fonction getimagesize() est ton amie :
http://fr.php.net/getimagesize
28. Par xsaiddx le lundi 18 mai 2009 à 22:10
ouii j'ai deja vu la doc mais le blem que je pige rien
bon pour quoi tu compare $infoImg[2] avec 1 et qu'est ce qu'il ya dans $imgInfo[2]
merci
29. Par Emacs le lundi 18 mai 2009 à 23:11
L'index 2 contient le type de l'image numéroté de 1 à 14. On vérifie que l'image dont on récupère les infos est bien une image dont le type est prédéfini par le développeur. Nous aurions pu limiter le test aux types GIF, JPG, PNG et BMP par exemple.
30. Par jaymi le samedi 20 juin 2009 à 16:04
j'ai un problème avec certaines images d'extension .JPG qui ne sont pas acceptées. j'ai une réponse qui me dit que le fichier n'est pas une image pourtant ça l'est
31. Par alibaba le vendredi 03 juillet 2009 à 18:06
Salut Emacs!
Avez-vous testé ce script, car il ne crée pas le répertoire sible s'il n'existe pas.
Quand je supprime le '/' qui precède le nom du repertoire c-à-d, en faisant ceci : define('TARGET', 'files/'); ça fonctionne correctement(le script crée le répertoire et il envoie(upload) aussi l'image).
Merci pour ce tuo, il est top!!!


1. Par prout le mardi 04 décembre 2007 à 22:10