Bonjour,

Voilà je suis actuellement en développement sur un projet perso, et j'ai un problème sur un upload d'image.
J'ai une page pour créer un compte et l'upload d'image fonctionne sans problème.
Le problème apparaît sur la page du compte pour mettre à jour l'image. J'ai repris le système d'upload de ma création de compte en modifiant la requête. Mon problème est simple :

<form action="../App/changePp.php" method="post" enctype="multipart/form-data">
        <label for="imagep">Veuillez sélectionner votre nouvelle photo</label>
        <input type="file" name="imagep"/><br/>
        <button type="submit" id="btnprofile" style="width:210px">Changer ma photo de profil</button>
      </form>

Voici mon formulaire, et quand j'upload, j'ai 2 possibilités :
Si j'ai l'enctype sur le form (pour upload une image et non pas le juste le nom du fichier), le $_POST est vide. (testé avec un var_dump($_POST))
Si je retire l'enctype, l'upload se fait correctement mais quand j'essaie de récupérer les données de l'image avec par exemple un $_FILES['image']['name'] j'ai des messages d'erreur, comme quoi les variables n'existent pas (la variable image en l'occurence).
Je n'ai jamais eu ce problème et c'est surement tout simple, mais j'avoue que là je bloque pas mal.
Si quelqu'un a une idée, je suis prenneur, si vous avez besoin de voir plus de code, n'hésitez pas, et merci d'avance ! :)

24 réponses


Bonjour,
Vu que ton bouton "submit" n'a pas de name alors tu n'auras aucun $_POST mais uniquement le $_FILES.

Je teste ça, je pensais que le post se faisait sur l'input et non pas le bouton ^^'
Ce qui me semble bizarre c'est que

<form action="../App/changePass.php" method="post" style="width: 210px;">
             <div class="form-group">
               <input class="form-control" type="password" name="password" placeholder="Nouveau mot de passe"/>
             </div>
             <div class="form-group">
               <input class="form-control" type="password" name="password_confirm" placeholder="Confirmation du mot de passe"/>
             </div>
             <button id="btnpass" style="width:210px">Changer mon mot de passe</button>
           </form>

Fonctionne bien alors que le bouton n'a pas de name :x

Retour du $_POST :
array (size=1)
'imagep' => string '' (length=0)
après avoir attribué un name au bouton

Oui il se fait aussi avec un input. Le bouton n'est-il pas un input de type submit ?

Et que retourne $_FILES ?

Le bouton est un <'button> j'ai rajouté l'attribut submit parce que ça ne fonctionnait pas
$_FILES retourne le bon fichier j'ai l'impression
array (size=1)
'imagep' =>
array (size=5)
'name' => string '20160731_215615176_iOS.jpg' (length=26)
'type' => string 'image/jpeg' (length=10)
'tmp_name' => string 'C:\wamp64\tmp\php5087.tmp' (length=25)
'error' => int 0
'size' => int 580629

Donc c'est bon, tu as bien ton fichier.

Mais du coup c'est normal que $_POST est vide ? Quand je faisais la création de compte le $_POST contenait d'autres informations comme le pseudo ou l'email, mais il contenait aussi l'image.

oui parce que tu avais des champs input de remplit. Si tu n'as pas de champs à remplir alors il n'y a pas de $_POST
Je doute que $_POST contenait l'image

D'accord, le code fonctionnait surement parce qu'il contenait d'autres éléments, du coup mon if(!empty($_POST)) laissait passer, et après j'utilisais $_FILES, je comprends mieux maintenant.
Dernière petite question, j'ai trouvé sur un site un code pour écraser le fichier si il existe déjà dans le dossier, histoire de ne pas avoir 500 fichiers si l'utilisateur s'amuse à changer de photo

     if (file_exists($content_dir.$_FILES['imagep']))
      {
      unlink($content_dir.$_FILES['imagep']);
      }

Le problème c'est que l'execution me retourne
Warning: unlink(../App/Res/profile/): Permission denied in D:\Mega\PHP\Projet\App\changePp.php on line 17

Il y a un moyen de forcer l'execution ? ma session est en administrateur et le répertoire est en cloud.

La je suis moins spécialiste.
Tu dis que le dossier est en cloud donc as-tu les froits pour supprimer un fichier. CHMOD 777 ( tout accès)

Non je n'ai aucun chmod ou autre, à quel endroit l'utiliser ? Sur quelle variable ?

Bonjour.
Avant de dire :

Non je n'ai aucun chmod ou autre

Est-ce que tu sais ce que c'est au moins ?
Pour information il faut au moins avoir un accès SSH pour utiliser les lignes de commandes pour un serveur distant.
Vu qu'après tu demandes :

Sur quelle variable ?

Je ne pense pas que tu saches ce que c'est, étant donné que ce n'est pas un niveau PHP.

Vérifier que tu as les droits du dossier parent.

l'erreur "Access denied" c'est parceque tu essayes d'effacer le dossier
il faut utiliser le nom du fichier tel qu'il est donné par l'utilisateur

if (file_exists($content_dir . $_FILES['imagep']['name']))
  {
      unlink($content_dir . $_FILES['imagep']['name']);
  }

Je n'ai plus l'erreur d'accès, je ne sais pas pourquoi.
En revanche quand je change ma photo par un fichier .png ça ne l'écrase pas. (D'ailleurs même si le profil n'a aucune photo, les .png n'ont pas l'air de fonctionner.)

<?php
namespace app;
require '../App/Autoloader.php';
require '../App/Functions.php';
Autoloader::register();
$db = new Database('projetphp');
$fc = new Functions;
$fc->logged_only();
 if(!empty($_FILES)){
     $errors = array();
     $id = $_SESSION['auth']['id'];
     $content_dir = '../App/Res/profile/';
     var_dump($_FILES);
     var_dump($_SESSION);
     if (file_exists($content_dir.$_FILES['imagep']['name'].'.jpg') || file_exists($content_dir.$_FILES['imagep']['name'].'.png') || file_exists($content_dir.$_FILES['imagep']['name'].'.jpeg'))
      {
      unlink($content_dir.$_FILES['imagep']['name'].'.jpg');
      unlink($content_dir.$_FILES['imagep']['name'].'.png');
      unlink($content_dir.$_FILES['imagep']['name'].'.jpeg');
      }

               $tmp_file = $_FILES['imagep']['tmp_name'];
               if( !is_uploaded_file($tmp_file) )
               {
                   $errors['imagep'] = 'photo introuvable';
               }

               $type_file = $_FILES['imagep']['type'];

               if( !strstr($type_file, 'jpg') && !strstr($type_file, 'jpeg') && !strstr($type_file, 'png'))
               {
                   $errors['imagep'] = 'Le format de l\'image n\'est pas valide. Veuillez utiliser le format .jpg, .jpeg ou .png !';
               }

                 $fext = $_FILES['imagep']['name'];
                 $fileLength = strlen($fext) - 3;
                 $fext = substr($fext, $fileLength);
                 $name_file = strtolower($_SESSION['auth']['username'] . '.' . $fext);

               if( !move_uploaded_file($tmp_file, $content_dir . $name_file) )
               {
                   $errors['imagep'] = 'Impossible de copier la photo dans '.$content_dir;
               }

               if($type_file === 'jpg'){
                 $fc->correctImageOrientation($content_dir . $name_file);
               }
               $image = $name_file;
   if(empty($errors)){
   $db->updatepp($image, $id);
   //header('Location: ../Public/index.php?p=account');
   exit();
   }
   else{
     $_SESSION['errors'] = $errors;
     var_dump($errors);
     //header('Location: ../Public/index.php?p=home');
   }

 }

 ?>

et pour la fonction correctImageOrientation, elle permet de pivoter la photo si elle est issue d'un appareil mobile (les photos iphone étaient pivotées à 90° par défaut)

function correctImageOrientation($filename) {
  if (function_exists('exif_read_data')) {
    $exif = exif_read_data($filename);
    if($exif && isset($exif['Orientation'])) {
      $orientation = $exif['Orientation'];
      if($orientation != 1){
        $img = imagecreatefromjpeg($filename);
        $deg = 0;
        switch ($orientation) {
          case 3:
            $deg = 180;
            break;
          case 6:
            $deg = 270;
            break;
          case 8:
            $deg = 90;
            break;
        }
        if ($deg) {
          $img = imagerotate($img, $deg, 0);
        }
        // then rewrite the rotated image back to the disk as $filename
        imagejpeg($img, $filename, 95);
      } // if there is some rotation necessary
    } // if have the exif orientation info
  } // if function exists
}

Normalement avec ce code les images .png devraient passer, je ne comprend pas le problème. Elle s'upload bien mais elle n'est pas chargée sur le profil.
Pourtant quand je récupère l'image je ne précise pas l'extension :

<img id="accprofile" src="../App/Res/profile/<?=$_SESSION['auth']['profile'] ?>">

edit : Quand je change l'image du profil, elle se remplace bien seulement si l'extension est la même.
pseudo.jpg si je la change avec un autre .jpg elle l'écrasera, mais si j'upload un .png il recréera un pseudo.png, et il y aura 2 photos et donc le .png ne sera pas lié.
edit 2: quand j'upload un .png aucune photo de profil n'apparaît et en vérifiant le code source, c'est un .jpg.
Dans le var_dump j'ai trouvé ça
'name' => string '402.png' (length=7)
'type' => string '' (length=0)
'tmp_name' => string '' (length=0)
'error' => int 1
'size' => int 0
En gros l'extension n'est pas retenue.
pourtant je précise
!strstr($type_file, 'png') dans une condition pour fixer l'upload à 3 formats de fichiers (png, jpg et jpeg)

Bonjour, je viens rajouter mon grain de sel :D

en fait c'est presque normal. j'ai pas tout vu mais directe, ca, ca m'a sauté aux yeux

 if (file_exists($content_dir.$_FILES['imagep']['name'].'.jpg') || file_exists($content_dir.$_FILES['imagep']['name'].'.png') || file_exists($content_dir.$_FILES['imagep']['name'].'.jpeg'))
      {
      unlink($content_dir.$_FILES['imagep']['name'].'.jpg');
      unlink($content_dir.$_FILES['imagep']['name'].'.png');
      unlink($content_dir.$_FILES['imagep']['name'].'.jpeg');
      }

tu te base sur le nom de la photo uploadé pour supprimer un ancien fichier ? Oo et si mes fichiers ont des noms différents ? ca marche pas. ensuite, tu test un fichier et tu veux en supprimer 3 ... pas cool.
C'est moche, mais si tu veux masquer les eventuelles erreurs, tu peux mettre un @ devant tes fonction unlink(). -> pas de levé d'exception.

Ce qui serait bien de faire, c'est d'enregistrer le nom de l'image avec l'extention en BDD (comme ca, t'es sur de pas te tromper et tu pourras virer ta condition toute moche). tu supprime l'ancienne quand tu es sur que la nouvelle a bien été ecrite et tu met a jour ton utilisateur avec le nom de la nouvelle image.

pour ce qui est de :

 if( !strstr($type_file, 'jpg') && !strstr($type_file, 'jpeg') && !strstr($type_file, 'png'))
       {
           $errors['imagep'] = 'Le format de l\'image n\'est pas valide. Veuillez utiliser le format .jpg, .jpeg ou .png !';
       }

tu peux le transformer en :

 if( ! in_array($type_file, array('jpg', 'jpeg', 'png')))

et dernièrement, pour le mauvais affichage de ta photo, dans ta vue tu mettrai pas en dur le '.jpg' ?

J'ai résolu mon problème tout seul en fait, les unlink n'étaient pas bons puisqu'il cherchait à supprimer les 3 fichiers donc erreur quand il ne trouvait pas les 3 images.

if (file_exists($content_dir.$_SESSION['auth']['username'].'.jpg'))
      {
        unlink($content_dir.$_SESSION['auth']['username'].'.jpg');
      }

    if (file_exists($content_dir.$_SESSION['auth']['username'].'.png'))
      {
        unlink($content_dir.$_SESSION['auth']['username'].'.png');
      }

    if (file_exists($content_dir.$_SESSION['auth']['username'].'.jpeg'))
      {
        unlink($content_dir.$_SESSION['auth']['username'].'.jpeg');
      }

j'ai donc fait 3 unlink x)
Et le problème que j'avais avec l'image qui ne chargeait pas c'était à cause des cookies qui ne se mettaient pas à jour directement, en redémarrant le navigateur ça fonctionnait, j'ai donc modifié juste après l'ajout dans la base le nom du fichier à la session
$_SESSION['auth']['profile']=$image;
Et pour le nom du fichier pour supprimer l'ancien c'est simple, pour éviter de polluer mes dossiers j'ai fait pour que le nom du fichier soit pseudo.ext, donc si l'utilisateur change de photo, ça supprime directement l'ancienne peu importe son extension (les pseudos sont uniques, donc aucun risque de supprimer l'image de quelqu'un d'autre.) et ça la remplace par la nouvelle.

Tu sais bien que tout le monde nomme sa photo "photo.jpg" :)
il ne faut pas s'y fier mais générer ton propre nom de fichier par un timestamp ou bien l'id du user (à creuser)

Justement si tout le monde renomme photo.jpg ça me va, comme j'ai dit juste au dessus quand tu upload ça choppe ton username dans $_SESSION et ça le renomme username.ext ça permet de n'avoir qu'une seule photo par utilisateur, avec un nom précis et c'est simple pour se retrouver au moins. :p

et dans ton appli, tu peux changer de nom ? :p

Non, impossible de changer d'username ni de prénom/nom, tu peux changer de mot de passe, de photo de profil et de CV. :p Sinon ça serait facile de polluer le dossier avec les photos ça je l'ai bien compris :p

Bonjour.
Je te conseille de remplacer :

if (file_exists($content_dir.$_SESSION['auth']['username'].'.jpg'))
{
    unlink($content_dir.$_SESSION['auth']['username'].'.jpg');
}

if (file_exists($content_dir.$_SESSION['auth']['username'].'.png'))
{
    unlink($content_dir.$_SESSION['auth']['username'].'.png');
}

if (file_exists($content_dir.$_SESSION['auth']['username'].'.jpeg'))
{
    unlink($content_dir.$_SESSION['auth']['username'].'.jpeg');
}

Par :

$exist = $content_dir . $_SESSION['auth']['username'];
if (file_exists($exist . '.jpg')) {
    unlink($exist . '.jpg');
} elseif (file_exists($exist . '.png')) {
    unlink($exist . '.png');
} elseif (file_exists($exist . '.jpeg')) {
    unlink($exist . '.jpeg');
}

Beaucoup moins répétitif, et tu fais moins de condition dans un sens, vu que dès qu'une condition sera positive les autres ne seront pas inspectées, ce qui peut améliorer les performances de ton script.