Bonsoir !
Me revoilà avec un nouveau problème !

Continuant l'édition du profil, j'ai intégré un système d'avatar.
Celui-ci fonctionne comme ça : la personne upload son image et le script modifie le nom de l'image en fonction de son ID (stocké dans une variable SESSION) et va la mettre dans un dossier "avatars". Pour la bdd, la colonne avatar reçoit le nom (modifié) de l'image. Ainsi, il est facile pour moi de l'afficher par la suite.

Seulement, en le faisant tester à mon frère, il s'est aperçu d'un problème. A force d'upload des images différentes, les images qui s'affichaient n'avaient rien à voir avec ce qu'il venait de modifier mais étaient des images modifié plus tôt dans l'après-midi. J'ai alors regardé le dossier où sont stockés toutes les images et je me suis rendu compte qu'il modifie l'image dans le dossier uniquement lorsque l'extension est la même. Son compte ayant l'ID 4, on a donc 4.png, 4.jpg, 4.gif, etc et visiblement cela faisait buguer.

Voici le code d'upload :

if(isset($_FILES['avatar']) AND !empty($_FILES['avatar']['name']))
            {
                $tailleMax = 2097152;
                $extensionsValides = array('jpg', 'jpeg', 'gif', 'png');
                if($_FILES['avatar']['size'] <= $tailleMax)
                {
                    $extensionUpload = strtolower(substr(strrchr($_FILES['avatar']['name'], '.'), 1));
                    if(in_array($extensionUpload, $extensionsValides))
                    {
                        $chemin = "membres/avatars/".$_SESSION['id'].".".$extensionUpload;
                        $resultat = move_uploaded_file($_FILES['avatar']['tmp_name'], $chemin);
                        if($resultat)
                        {
                            $updateavatar = $bdd->prepare('UPDATE membres SET avatar = :avatar WHERE id = :id');
                            $updateavatar->execute(array(
                                'avatar' => $_SESSION['id'].".".$extensionUpload,
                                'id' => $_SESSION['id']
                                ));
                        }
                        else
                        {
                            $erreurs[] = "Une erreur est survenue pendant l'importation de votre photo.";
                        }
                    }
                    else
                    {
                        $erreurs[] = 'Votre photo de profil doit être au format jpg, jpeg, gif, png';
                    }
                }
                else
                {
                    $erreurs[] = 'Votre photo de profil ne doit pas dépasser 2Mo.';
                }

            }

Avez-vous une petite idée pour remédier à ce problème ?

Faut-il faire un système pour modifier automatiquement l'extension de l'image pour que toutes les images aient la même extension ? Si c'est cela, ne risque-t-on pas d'avoir une perte de qualité ?

Merci de vos réponses,
Boris95098

16 réponses


PhiSyX
Réponse acceptée

Yo, oui. La raison pour laquelle ça ne fonctionne pas, les fichiers n'ont pas été trouvés pour cause que le pattern est incorrect au niveau du glob.

# @example: Session id = 4
# `glob` te retourne tous les noms de tes fichiers avatars (correspondant à ta recherche) sous forme de tableau.
# @return array @example [0 => membres/avatars/4.jpg, 1 => membres/avatars/4.png]
$avatars = glob('membres/avatars/' . $_SESSION['id'] . '.{jpg,jpeg,gif,png}', GLOB_BRACE);
foreach ($avatars as $avatar) {
    # `unlink` prend en premier paramètre une `string` (filename).
    # Tu lui as donné un `array`.. (Déclenche une warning automatiquement, normalement.)
    unlink(realpath($avatar));
}

Voilà, ça devrait fonctionner correctement ^^ mais je répète SI le problème persiste, au cas où, c'est que le navigateur met en cache tes images (et donc voir ma premiere réponse.)

PhiSyX
Réponse acceptée

Il faut mettre le chemin relatif (non absolu) de l'image :-) soit 'membres/avatars/4.png' (tu peux le realpath pour avoir le chemin complet :-))

Yo.

A force d'upload des images différentes, les images qui s'affichaient n'avaient rien à voir avec ce qu'il venait de modifier mais étaient des images modifié plus tôt dans l'après-midi.

C'est sûrement que le navigateur met en cache les images. ^^
Rajoute une ?queryString à la fin de l'URL des avatars (Ex: <img src="4.png?<?= filemtime('4.png') ?>">) pour y remédier ^^

Sinon rien avoir avec ta question mais pense à "unlink" les avatars de l'utilisateur avant de rajouter le nouvel avatar. (Imaginons un utilisateur met un .gif, puis qu'il décide de changer pour y mettre un .png. Tu auras au final 2 avatars mais avec des extensions differentes dont une qui ne sert à rien ^^)

Salut ! Je te remercie grandement de ta réponse !

"Sinon rien avoir avec ta question mais pense à "unlink" les avatars de l'utilisateur avant de rajouter le nouvel avatar. (Imaginons un utilisateur met un .gif, puis qu'il décide de changer pour y mettre un .png. Tu auras au final 2 avatars mais avec des extensions differentes dont une qui ne sert à rien ^^)"

C'est EXACTEMENT le problème que j'ai mais en mieux expliqué ! Et c'est sûrement ça qui provoque les problèmes !
Comment puis-je unlink les images ? Je n'ai jamais fais ça (l'upload d'images n'étant pas mon fort) :)

Boris95098

Bonjour,

En fait "unlink les images" ça veut dire utiliser la fonction unlink de php pour supprimer un fichier. Je te laisse le soin de regarder sur la doc de PHP au sujet de cette fonction : PHP Doc - Unlink.

Yo, exactement.

Juste avant d'ajouter l'avatar téléchargé, avec move_uploaded_file, récupère tous les avatars de l'utilisateurs et supprime les. (Nul la réponse, non? :D)
Comment faire? Voici un peu les fonctions que tu peux utiliser pour réaliser celà: glob, array_walk/foreach, unlink et realpath. (Pareil, j'te laisse le soin de regarder...)
Sachant qu'il y a plusieurs façon de faire, je te donne la plus simple des façons (à mon sens) mais si quelqu'un a mieux, il peut toujours donner sa version.

Sinon au pire puisque les différentes extensions font buguer tu peux limiter les extensions style seulement png ou convertir les images en png

bonjour,
Evite de laisser l'id de la personne en clair passe le par un hachage type md5 ou quelque chose du genre cela renforcera le nom et t'evitera des problèmes et la taille de tes noms de photo sera identiques.

En se qui concerne ton problème, supprime les photos ayant le meme nom du dossier avant la copie de fichier ! et paf ca fait des chocapics !

cordialement

Bonsoir !

Désolé de ma réponse qui se fait tardive ! A l'approche des examens, j'ai eu quelques petits trucs qui m'ont pris beaucoup de temps mais dès ce soir, je m'y remet !

Je tiens à tous vous remercier de vos réponses, malheureusement je ne parviens toujours pas à obtenir ce dont j'ai envie.

J'ai testé le unlink qui me parait le plus approprié mais je ne comprends pas pourquoi, ça ne fonctionne pas....

Voilà ce que j'ai testé :

if(isset($_FILES['avatar']) AND !empty($_FILES['avatar']['name']))
            {
                $tailleMax = 2097152;
                $extensionsValides = array('jpg', 'jpeg', 'gif', 'png');
                if($_FILES['avatar']['size'] <= $tailleMax)
                {
                    $extensionUpload = strtolower(substr(strrchr($_FILES['avatar']['name'], '.'), 1));
                    if(in_array($extensionUpload, $extensionsValides))
                    {
                        $chemin = "membres/avatars/".$_SESSION['id'].".".$extensionUpload;
                        $fichier = glob('/profil/membres/avatars/'.$userinfo['avatar']);
                        if($userinfo['avatar'] != 'defaut.png')
                        {
                            unlink($fichier);
                        }

                        $resultat = move_uploaded_file($_FILES['avatar']['tmp_name'], $chemin);
                        if($resultat)
                        {
                            $updateavatar = $bdd->prepare('UPDATE membres SET avatar = :avatar WHERE id = :id');
                            $updateavatar->execute(array(
                                'avatar' => $_SESSION['id'].".".$extensionUpload,
                                'id' => $_SESSION['id']
                                ));
                        }
                        else
                        {
                            $erreurs[] = "Une erreur est survenue pendant l'importation de votre photo.";
                        }
                    }
                    else
                    {
                        $erreurs[] = 'Votre photo de profil doit être au format jpg, jpeg, gif, png';
                    }
                }
                else
                {
                    $erreurs[] = 'Votre photo de profil ne doit pas dépasser 2Mo.';
                }

            }

Mais j'ai également essayé de bien d'autres manières, en enlevant le glob() et en mettant uniquement le unlink, etc, etc mais rien à faire.. Si j'upload une image .png, j'aurai 1.png, ensuite si j'upload une image .jpeg, un autre fichier va se créer, nommé 1.jpeg

J'ai essayé la conversion d'image en PNG mais la librairie GD n'est carrément pas mon fort, j'ai vraiment du mal à la comprendre.
J'ai testé cela (à la place du unlink) :

imagepng("/profil/membres/avatars/".$_SESSION['id'].'.'.$extensionUpload);

mais ça ne fonctionne pas non plus..

N'auriez-vous pas une piste en plus à me donner ? Je sais que vous m'en avez déjà donné pas mal mais là, je suis vraiment bloqué...

Merci à tous !

Boris95098

Salut ! Merci de ta réponse !!

Je n'avais pas pensé à faire la boucle ! Grossière erreur de ma part.. je l'ai pourtant vu sur la doc mais j'ai pensé que cela ne me concernait pas. je faisais uniquement le glob() suivi d'une condition pour vérifier si le fichier existe et du coup, ça ne fonctionnait pas.

Je vais de toute façon je vais également faire ta première proposition pour supprimer tous les risques de bug :)

En tout cas, je te remercie vraiment ! Je test ça en rentrant et je te dis quoi ! :)

Boris95098

Parfaaaaait ! Avec des explications et tout ce qu'il faut ! Ca a fonctionné et je te remercie BEAUCOUP ! Pour être franc, je ne l'aurai jamais trouvé seul (malgré le fait que j'ai compris le code proposé). Etant encore débutant, surement une certaine logique de programmation que je n'ai pas encore.. :)

Pour l'histoire du cache, j'ai mis ça comme code sur la page où l'image de profil est affichée :

<img src="/profil/membres/avatars/<?php echo $userinfo['avatar']; ?>?<?php echo filemtime($userinfo['avatar']); ?>" width="150" />

Est-ce bien cela où est-ce qu'après le filemtime, je dois remettre tout le chemin de destination ? Sur la doc, il y avait des exmples plus ou moins indiqué comme le tien mais je ne suis pas tout à fait sur et bizarrement, après avoir mis ça dans l'image, sur chaque profil (le mien et celui de mon frère), les images prenaient un certain temps à charger, ce qui n'est plus arrivé après :)

En tout cas, je te remercie encore, encore et encore !! :)

Bonne soirée,
Boris95098

Voilà voilà !

<img src="/profil/membres/avatars/<?php echo $userinfo['avatar']; ?>?<?php echo filemtime('membres/avatars/$userinfo[\'avatar\']'); ?>" width="150" />

Je te remercie encore !! :)

filemtime('membres/avatars/' . $userinfo['avatar']) sinon la variable ne sera pas interpreté et tu auras une erreur. (C'est pour ça éventuellement que "les images prenaient un certain temps à charger")

Ah oui ! C'est logique ! Merci !! :)

Un petit conseil histoire de parler de sécurité. Quand quelqu'un upload un fichier sur TON serveur tu dois pouvoir gérer le fichier à 100%. Je m'explique, vérifier l'extension ou le mime-type n'est clairement pas suffisant (remember noelshack et autres qui avaient été déface à cause de skids utilisant TamperData pour modifier le mime-type / extension lors de l'envoie au serveur). De plus, il existe des techniques pour upload un fichier .php en le faisant passer pour un .gif, le coup "classique" du filename.php%0.gif (tout ce qui se trouve après le %0 étant supprimé lors de l'upload, ton script se retrouve bypass).

Pour remédier à celà et être sur d'avoir une sécurité maximale je te conseil deux choses:

  • Utiliser GD2/Imagemagik pour recréer l'image, ainsi il ne sera plus possible d'envoyer des codes malveillants sur ton serveur puisque le serveur s'occupera de générer l'avatar depuis le fichier que l'utilisateur a envoyé ;
  • Utiliser un sous-domaine (statics.domain.com ou uploads.domain.com) ne tournant pas sur ton serveur web et servant uniquement à afficher des fichiers statiques. En gros pas de PHP sur le serveur des fichiers statiques/avatars, comme ça même si quelqu'un arrive à bypass la protection GD2/Imagemagik, il ne pourra pas executer son script vu que le serveur s'occupe uniquement d'afficher le fichier, pas de l’exécuter/compiler/lire. (C'est ce que fait imgur d'ailleurs) :)

Salut ! Désolé pour la réponse tardive de nouveau.. :)

Le truc avec Imagemagick, c'est qu'en regardant sur la doc, je m'aperçois que c'est du orienté objet et je dois reconnaître avoir du mal dans ce domaine. :)

le static.domain.com servirait uniquement à traiter l'image alors ?

Comment dois-je procéder car je ne suis pas sur d'avoir tout compris :D

Mais merci en tout cas ! :)