Search code examples
phpmysqlimagefile-uploaduploading

how to combine foreach uploading files


i have an upload script with that i like to upload a couple of images to a directory.

i use this code:

$allowed_extension = array('jpg', 'jpeg', 'png', 'bmp', 'tiff', 'gif');
$errors = array();
$output = array();

if(!empty($_FILES['image']['tmp_name'])){  

     foreach($_FILES['image']['name'] as $key => $array_value){

        if(!in_array(pathinfo($_FILES['image']['name'][$key], PATHINFO_EXTENSION), $allowed_extension)){
                die("Die!");
        }
    }

    foreach($_FILES['image']['name'] as $key => $array_value){

       $file_name = $_FILES['image']['name'][$key];
       $file_size = $_FILES['image']['size'][$key];
       $file_tmp = $_FILES['image']['tmp_name'][$key];

       $file_extension = pathinfo($file_name, PATHINFO_EXTENSION);
       $file_extension = strtolower($file_extension);

         if (!in_array($file_extension, $allowed_extension)){
        $errors[$file_name][] = "format $file_extension in image $file_name is not accepted";
        continue;
         }

         if ($file_size > 2097152){
        $errors[$file_name][] = "maxsize of 2MB on $file_name has reached";
                    }

         if (count($errors) == 0){

        $dir = "a/b/c";

        if (is_dir($dir)){
            mkdir("a/b/c/tmp_images", 0755);
        }else{
            mkdir("a/b/c", 0755);
            mkdir("a/b/c/tmp_images", 0755);
        }

        $path = "a/b/c/tmp_images"; 
        $prifix = basename($file_name, "." . $file_extension);

        //var_dump ($prifix);

        $uploadfile = $path . "/" . $file_name;

        $x = 0;
        while (file_exists($uploadfile)){
               $x ++;
               $uploadfile = "{$path}/{$prifix}-{$x}.{$file_extension}";
            }

            if (move_uploaded_file($file_tmp, $uploadfile)){
               $file_name = basename($uploadfile);
               $output [$file_name] = "OK";

            }else{

            $output[$file_name] = "Failure while Uploading!";
            $errors[$file_name][] = "Failure: Can't move uploaded pictures!";
            }//else...
         }//if(count($errors))...
    }//foreach($_FILES['image']['name']... 
}//if(!empty($_FILES['image']['tmp_name'])... 

the problem is that part:

if(!empty($_FILES['image']['tmp_name'])){  

         foreach($_FILES['image']['name'] as $key => $array_value){

            if(!in_array(pathinfo($_FILES['image']['name'][$key], PATHINFO_EXTENSION), $allowed_extension)){
                    die("Die!");
            }
        }

in case of uploading two different kind of file types it will cancel the upload. this is my first problem. in such a case i need to cancel the upload because this is for a file that i like to build after posting the form. so the user has no possibility to make any changes after he has post the form. let's have a look at the second part:

foreach($_FILES['image']['name'] as $key => $array_value){

           $file_name = $_FILES['image']['name'][$key];
           $file_size = $_FILES['image']['size'][$key];
           $file_tmp = $_FILES['image']['tmp_name'][$key];

           $file_extension = pathinfo($file_name, PATHINFO_EXTENSION);
           $file_extension = strtolower($file_extension);

             if (!in_array($file_extension, $allowed_extension)){
            $errors[$file_name][] = "format $file_extension in image $file_name is not accepted";
            continue;
             }

             if ($file_size > 2097152){
            $errors[$file_name][] = "maxsize of 2MB on $file_name has reached";
                        ...}

here it can be seen that it actually should be approached that an error message should have been displayed. if i leave the first foreach-part then i got the problem that in case of two different file types one file will be uploaded and for the other one the errormessage will be displayed.

so there should be a way to combine the first and the second foreach. if there is someone who could tell me the right way i really would appreciate. thanks a lot.

UPDATE

okay, sorry for my lateness. here my feedback: this code seems to have some problems in case of two different file types. it seems like

if (count($errors)>0){
   die ("...")
} 

does not really count. in case of that it will get the error message:

Warning: move_uploaded_file() [function.move-uploaded-file]: Unable to access a/b/c/tmp_images/abc.xls

but it will also do an upload for the image that has an allowed extension but this can't be successfull because of the wrong filetype of the other one, i guess. the problem is that this causes the insert query that is placed here:

if (move_uploaded_file($fileTmp, $fileDst)) {
            $output[$fileBase] = "OK";

            **$insert = $db->query("INSERT INTO table ( a, b, c) VALUES('".$a."','".$b."','".$c."')");{

            mkdir("a/b/c/d/e", 0755);
            rename("a/b/c/tmp_images", "a/b/c/d/e/tmp_images");

            $msg ="text abc.";                          

            $to = "xxx@xxx.xx";
            $subject = "xxx ";
            $message = "text";

            $headers  = "From: xxx<xxx@xxx.xx>\r\n";
            $headers .= "Content-type: text/plain; charset=UTF-8\r\n";
            $mailheader .= "Return-Path: failuremail@" .$_SERVER['SERVER_NAME']. "\r\n";
            $mailheader .= "MIME-Version: 1.0\r\n";
            $mailheader .= "Content-Transfer-Encoding: 7bit\r\n";
            $mailheader .= "Message-ID: <" .time(). " noreply@" .$_SERVER['SERVER_NAME']. ">\r\n";
            $mailheader .= "X-Mailer: PHP v" .phpversion(). "\r\n\r\n";

            mail($to, $subject, $message, $headers);

            }//**

            }else{

                $output[$fileBase] = "Fehler beim Upload des Bildmaterials!";
                $errors2[$fileBase][] = "Fehler: Die hochgeladenen Dateien können nicht verschoben werden!";
            }//             
        }//

        if (count($errors) > 0) {
            die("Failurecode: ");
            echo "$errors";
    }...

and i would like to approach to get the error messages from the 2 dimensional array the same way like i get them from my other error messages that comes from one dimensional arrays at the same page as the formular will be. in case of "die" it will open a new empty page to show the error. thanks a lot.


Solution

  • You will need two loops for this, but the approach you have taken is slightly flawed. You need a validation loop which performs all the validation for all the files, then a second loop which just moves the files to permanent storage.

    Something like this (FIXED):

    <?php
    
      // Settings
      $allowedExtensions = array('jpg', 'jpeg', 'png', 'bmp', 'tiff', 'gif');
      $maxSize = 2097152;
      $storageDir = 'a/b/c/tmp_images';
    
      // Result arrays
      $errors = $output = array();
    
      if (!empty($_FILES['image'])){  
    
        // Validation loop (I prefer for loops for this specific task)
        for ($i = 0; isset($_FILES['image']['name'][$i]); $i++) {
    
          $fileName = $_FILES['image']['name'][$i];
          $fileSize = $_FILES['image']['size'][$i];
          $fileErr = $_FILES['image']['error'][$i];
          $fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
    
          // Validate extension
          if (!in_array($fileExt, $allowedExtensions)) {
            $errors[$fileName][] = "Format $fileExt in image $fileName is not accepted";
          }
    
          // Validate size
          if ($fileSize > $maxSize) {
            $errors[$fileName][] = "$fileName excedes the maximum file size of $maxSize bytes";
          }
    
          // Check errors
          if ($fileErr) {
            $errors[$fileName][] = "$fileName uploaded with error code $fileErr";
          }
    
        }
    
        // Handle validation errors here
        if (count($errors) > 0) {
          die("Errors validating uploads: ".print_r($errors, TRUE));
        }
    
        // Create the storage directory if it doesn't exist
        if (!is_dir($storageDir)) {
          if (!mkdir($storageDir, 0755, TRUE)) { // Passing TRUE as the third argument creates recursively
            die("Unable to create storage directory $storageDir");
          }
        }
    
        // File move loop
        for ($i = 0; isset($_FILES['image']['name'][$i]); $i++) {
    
          // Get base info
          $fileBase = basename($_FILES['image']['name'][$i]);
          $fileName = pathinfo($fileBase, PATHINFO_FILENAME);
          $fileExt = pathinfo($fileBase, PATHINFO_EXTENSION);
          $fileTmp = $_FILES['image']['tmp_name'][$i];
    
          // Construct destination path
          $fileDst = $storageDir.'/'.basename($_FILES['image']['name'][$i]);
          for ($j = 0; file_exists($fileDst); $j++) {
            $fileDst = "$storageDir/$fileName-$j.$fileExt";
          }
    
          // Move the file    
          if (move_uploaded_file($fileTmp, $fileDst)) {
            $output[$fileBase] = "Stored $fileBase OK";
          } else {
            $output[$fileBase] = "Failure while uploading $fileBase!";
            $errors[$fileBase][] = "Failure: Can't move uploaded file $fileBase!";
          }
    
        }
    
        // Handle file move errors here
        if (count($errors) > 0) {
          die("Errors moving uploaded files: ".print_r($errors, TRUE));
        }
    
      }