Upload woes

I have a form which allows 6 uploads,


<table style="width:90%; margin:0 auto" class="form">
<tr>
<td><label for='file1'>Image 1:</label></td><td><input type='file' name='file[]' id='file1'></td>
<td><label for='file2'>Image 2:</label></td><td><input type='file' name='file[]' id='file2'></td>
</tr><tr>
<td><label for='file3'>Image 3:</label></td><td><input type='file' name='file[]' id='file3'></td>
<td><label for='file4'>Image 4:</label></td><td><input type='file' name='file[]' id='file4'></td>
</tr><tr>
<td><label for='file5'>Image 5:</label></td><td><input type='file' name='file[]' id='file5'></td>
<td><label for='file6'>Image 6:</label></td><td><input type='file' name='file[]' id='file6'></td>
 </table>

and im trying to upload them.
Heres my php code the form is submitted to


//function to return an array of only the uploaded files
function get_files($_FILES) {
    $files = $_FILES['file'];
    
    $file_count=count($files);
    $count=0;
    
    while ( $count <> $file_count-1 ) {
        $sorted_files[$count]['name']=$files['name'][$count];
        $sorted_files[$count]['type']=$files['type'][$count];
        $sorted_files[$count]['tmp_name']=$files['tmp_name'][$count];
        $sorted_files[$count]['error']=$files['error'][$count];
        $sorted_files[$count]['size']=$files['size'][$count];
        if (!is_uploaded_file($sorted_files[$count]['tmp_name'])) {
            unset($sorted_files[$count]);
        }
        $count++;
    }
    return $sorted_files;
}


get_files();

foreach ($sorted_files as $file) {  

  if ($file == '') {
    continue;
  }

  $Type = $file["type"];
  $Name = $file["name"];
  $tmp_name = $file["tmp_name"];
  $Error = $file["error"];
  $Size = $file["size"];
  echo $Type;
  echo "<br>";
  echo $Name;
  echo "<br>";
  echo $tmp_name;
  echo "<br>";
  echo $Error;
  echo "<br>";
  echo $Size;
  echo "<br>";
  if($Error > 0){
      echo 'An error ocurred when uploading.';
  }
  if(($Type != 'image/png') && ($Type != 'image/gif') && ($Type != 'image/jpeg') && ($Type != 'image/bmp'))
  {
      echo 'Unsupported filetype uploaded.';
  }
  if($Size > 102400){
      echo 'File uploaded exceeds maximum upload size.';
  }
  $now = time(); 
  while(file_exists($uploadFilename = $now.'-'.$Name)) 
  { 
      $now++; 
  } 
  if(!move_uploaded_file($tmp_name, 'uploads/'.$uploadFilename)){
      echo 'Error uploading file - check destination is writeable.';
  } 
$message .= "\
<tr><td>Image</td><td colspan='2'><input type='text' value='".$uploadFilename."' name='file[]' size='40'><br>Type: <b>".$Type."</b><br>Size: <b>".intval(($Size / 1024))." KB</b></td>";
$message .= "<td><img src='http://shores-rentals.com/rentals//uploads/".$uploadFilename."' width='150'></td></tr>\
";
}  
?>

But nothing seems to get printed to the screen if I try to upload a file (image)

Don’t see your form tags but make sure you have enctype=“multipart/form-data” in your <form> tag, e.g.

<form action="" method="post" enctype="multipart/form-data">

You probably need to also add multiple to each input line, e.g.

<input type='file' name='file[]' id='file1' multiple >

ok

Also note that uploaded files only exist in a temporary place on the server and need to be moved using rename so that they exist in a location on the server that is accessible.

I thought this moved an uploaded file


  if(!move_uploaded_file($tmp_name, 'uploads/'.$uploadFilename)){
      echo 'Error uploading file - check destination is writeable.';
  } 



It doesn’t?

Yes that does move the file.

I didn’t see that in your code until I scrolled further down it.

 if (is_uploaded_file($sorted_files[$count]['tmp_name']))
 if(!move_uploaded_file($tmp_name, 'uploads/'.$uploadFilename))
   $errorMessage = "Error uploading file - check destination is writeable.";

then output error messages or try a routine that uses chmod() to try changing the permissions of that folder that the file is going to and then reverting them back to previous values.

You might want to consider rejigging your code so that any upload errors are discovered earlier because you are echoing a load of information that really is not needed, all the user is interested in is if the file goes up to the server and your back end only needs to know that it is a file type that is accepted, you may not want the user knowing where the file is actually going to so if it was a malicious attack and someone uploads a PHP script then calls it or uploads an EXE file and then gets it to run or people download it and run it, etc. Consider security aspects of your server and the end users.

ok, did some reworking of the code, let me know if it looks ok


//run the function to get only the selected files (out of the 6 possible)
get_files();

foreach ($sorted_files as $file) {  

  if ($file == '') {
    continue;
  }

  $Type = $file["type"];
  $Name = $file["name"];
  $tmp_name = $file["tmp_name"];
  $Error = $file["error"];
  $Size = $file["size"];
  // first we check to see f en error code happened on the file
  if($Error > 0){
      echo 'An error ocurred when uploading.';
  }
  // then we check the extension
  if(($Type != 'image/png') && ($Type != 'image/gif') && ($Type != 'image/jpeg') && ($Type != 'image/bmp'))
  {
      echo 'Unsupported filetype uploaded.';
  }
  // Then we check its size
  if($Size > 102400){
      echo 'File uploaded exceeds maximum upload size.';
  }
  if alls good, we rename it to give it a unique name
  $now = time(); 
  while(file_exists($uploadFilename = $now.'-'.$Name)) 
  { 
      $now++; 
  } 
  // lastly we move the renamed file into my directory
  if(!move_uploaded_file($tmp_name, 'uploads/'.$uploadFilename)){
      echo 'Error uploading file - check destination is writeable.';
  } 
}  

This seems pretty good but I really dont understand the functkion


function get_files($_FILES) {
    $files = $_FILES['file'];
    
    $file_count=count($files);
    $count=0;
    
    while ( $count <> $file_count-1 ) {
        $files[$count]['name']=$files['name'][$count];
        $files[$count]['type']=$files['type'][$count];
        $files[$count]['tmp_name']=$files['tmp_name'][$count];
        $files[$count]['error']=$files['error'][$count];
        $files[$count]['size']=$files['size'][$count];
        if (!is_uploaded_file($sorted_files[$count]['tmp_name'])) {
            unset($sorted_files[$count]);
        }
        $count++;
    }
    return $sorted_files;
}


I dont think the function even works.

Simplest way to look at this is a very basic image upload form.

filename: form.php

<?php
// get some orientation going on...
$dirSelf = str_replace(basename($_SERVER['PHP_SELF']), '', $_SERVER['PHP_SELF']);
// generate the name of the upload processing script.
$handler = 'http://' . $_SERVER['HTTP_HOST'] . $dirSelf . 'processor.php';
// set a maximum file size allowed, this can also be set from use of the php ini function
$max_file_size = 1000000; // size in bytes
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
<title>Upload form</title>
</head>
<body>    
<form id="Upload" action="<?php echo $handler; ?>" enctype="multipart/form-data" method="post">
        <h1>Upload form</h1>
        <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $max_file_size ?>">
        
        <p><label for="file">Select File:</label>
            <input id="file" type="file" name="ulf"></p>
        <p><input id="submit" type="submit" name="submit" value="Submit"></p>
    </form>
    </body>
</html>

filename: processor.php

<?php
// make a note of the current working directory, relative to root.
$dirSelf = str_replace(basename($_SERVER['PHP_SELF']), '', $_SERVER['PHP_SELF']);

// make a note of the directory that will recieve the uploaded file
$uploadsDir = $_SERVER['DOCUMENT_ROOT'] . $dirSelf . 'upload/';

// make a note of the location of the upload form in case we need it
$uploadForm = 'http://' . $_SERVER['HTTP_HOST'] . $dirSelf . 'form.php';

// make a note of the location of the success page
$uploadSuccess = 'http://' . $_SERVER['HTTP_HOST'] . $dirSelf . 'success.php';

// fieldname used within the file <input> of the HTML form
$fieldname = 'ulf'; // name of the field on the upload form.

// inital security check was the upload form was actually submitted?
isset($_POST['submit']) or header("Location: {$uploadForm}");

// any uploading errors
($_FILES[$fieldname]['error'] == 0) or  header("Location: {$uploadForm}");
    
// check that the file was a HTTP upload
@is_uploaded_file($_FILES[$fieldname]['tmp_name']) or header("Location: {$uploadForm}");

// we are only accepting images...
@getimagesize($_FILES[$fieldname]['tmp_name']) or header("Location: {$uploadForm}");

// the file name
$uploadFilename = $uploadsDir.$_FILES[$fieldname]['name'];
// move the file
@move_uploaded_file($_FILES[$fieldname]['tmp_name'], $uploadFilename) or die("receiving directory insuffiecient permission");

// we did it!
header("Location: {$uploadSuccess}");
?>

filename: success.php

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
<title>Successful upload</title>
</head>
<body>
<h1>File upload</h1>
<p>Your file upload was successful, Thank you.</p>
</body>
</html>

Breaking the script down…

form.php will deal with the file selection
processor.php will do the legwork of checking that the form was used and that processor.php was not called directly and that the file being uploaded is an image and so on
success.php will be displayed if the file was uploaded successfully.

So going by that example, you should be able to get a working form, granted its a nofrills format but we are not here for pretty forms, just the facts.

I hope this helps you get a working form and you understand that this is as about as basic you can get and that you would need to have a few quirks added for people who might upload a file of the same name and changes that need to be made if you are accepting other file types like rar and zip files for example, you would need to employ the services of the is_executable() function to guard against executable file types like shell scripts, exe files and so on.

With a multiple file upload then you need to have a processor.php script like

<?php

// make a note of the current working directory, relative to root.
$dirSelf = str_replace(basename($_SERVER['PHP_SELF']), '', $_SERVER['PHP_SELF']);

// make a note of the directory that will recieve the uploaded file
$uploadsDir = $_SERVER['DOCUMENT_ROOT'] . $dirSelf . 'upload/';

// make a note of the location of the upload form in case we need it
$uploadForm = 'http://' . $_SERVER['HTTP_HOST'] . $dirSelf . 'form.php';

// make a note of the location of the success page
$uploadSuccess = 'http://' . $_SERVER['HTTP_HOST'] . $dirSelf . 'success.php';

// fieldname used within the file <input> of the HTML form
$fieldname = 'ulf'; // name of the field on the upload form.

// inital security check was the upload form was actually submitted?
isset($_POST['submit']) or header("Location: {$uploadForm}");

// were files uploaded, if so then store the active $_FILES keys
$keys = array();
foreach($_FILES[$fieldname]['name'] as $key => $filename)
    if(!empty($filename))
		$keys[] = $key;

// check at least one file was uploaded
count($active_keys) or header("Location: {$uploadForm}");
        
// check for uploading errors
foreach($keys as $key)
    ($_FILES[$fieldname]['error'][$key] == 0) or header("Location: {$uploadForm}");
    
// check that the file(s) are a HTTP upload
foreach($keys as $key)
    @is_uploaded_file($_FILES[$fieldname]['tmp_name'][$key]) or header("Location: {$uploadForm}");
    
// validation... since this is an image upload script we 
// should run a check to make sure the upload is an image
foreach($keys as $key)
    @getimagesize($_FILES[$fieldname]['tmp_name'][$key]) or header("Location: {$uploadForm}");

// now let's move the file to its final and allocate it with the new filename
foreach($keys as $key)
    @move_uploaded_file($_FILES[$fieldname]['tmp_name'][$key], $uploadFilename[$key]) or header("Location: {$uploadForm}");

header("Location: {$uploadSuccess}");
?>

In your script you appear to be missing the additional array dimension because the multiple files add an extra element to the $_FILES array.

thanks


function get_files($_FILES) {
    $files = $_FILES['file'];
    
    $file_count=count($files);
    $count=0;
    
    while ( $count <> $file_count-1 ) {
        $files[$count]['name']=$files['name'][$count];
        $files[$count]['type']=$files['type'][$count];
        $files[$count]['tmp_name']=$files['tmp_name'][$count];
        $files[$count]['error']=$files['error'][$count];
        $files[$count]['size']=$files['size'][$count];
        if (!is_uploaded_file($sorted_files[$count]['tmp_name'])) {
            unset($sorted_files[$count]);
        }
        $count++;
    }
    return $sorted_files;
}  

Lots of things going on here that can be simplified or are just plain wrong.

  • The function expects a parameter $_FILES but you call it without a parameter. Remove the parameter, or actually pass $_FILES to it.
  • The function doesn’t check if files are uploaded at all, it just assumes they are
  • $files = $_FILES[‘file’] is quite useless, you could just as well work with $_FILES[‘file’] directly. In fact, it would be easier.
  • $sorted_files is not initialised, which results in an E_NOTICE warning – which you seem to have turned off. I recommend you turn those on, it makes life a lot easier in the long run.
  • The loop would be a lot better with a for here instead of a while

function get_files() {
    if (empty($_FILES['file'])) return false;
    
    $sorted_files = array();
    for ($i = 0; $i < count($_FILES['file']); $i++) {
        if (!is_uploaded_file($_FILES['file']['tmp_name'][$i])) {
            continue;
        }
        foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
            $sorted_files[$i][$field] = $_FILES['file'][$field][$i];
        }
    }
    return $sorted_files;
}

A test of both methods using:

<?php

error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE | E_RECOVERABLE_ERROR);
ini_set('display_errors', 1);

echo "

              <form id='file_upload' action='' method='post' enctype='multipart/form-data'>
                  <lable>Select a file to upload: <input type='file' id='upload[]' name='upload[]'/></label>
                  <lable>Select a file to upload: <input type='file' id='upload[]' name='upload[]'/></label>
                  <lable>Select a file to upload: <input type='file' id='upload[]' name='upload[]'/></label>
                  <lable>Select a file to upload: <input type='file' id='upload[]' name='upload[]'/></label>
                  <lable>Select a file to upload: <input type='file' id='upload[]' name='upload[]'/></label>
                  <input type='hidden' name='action' value='upload_file'/>
                  <input type='submit' value='Upload'/>
              </form>
              
";

echo 'Before';
var_dump($_FILES);

// Method 1
echo 'Method 1';
$da_files_1=get_files();
var_dump($da_files_1);

//Method 2
echo 'Method 2';
$da_files_2=get_da_files();
var_dump($da_files_2);

function get_files() {
    if (empty($_FILES['upload'])) return false;
    
    $sorted_files = array();
    for ($i = 0; $i < count($_FILES['upload']); $i++) {
        if (!is_uploaded_file($_FILES['upload']['tmp_name'][$i])) {
            continue;
        }
        foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $field) {
            $sorted_files[$i][$field] = $_FILES['upload'][$field][$i];
        }
    }
    return $sorted_files;
}

function get_da_files() {
    $files = $_FILES['upload'];
    
    $file_count=count($files);
    $count=0;
    
    while ( $count <> $file_count ) {
        $sorted_files[$count]['name']=$files['name'][$count];
        $sorted_files[$count]['type']=$files['type'][$count];
        $sorted_files[$count]['tmp_name']=$files['tmp_name'][$count];
        $sorted_files[$count]['error']=$files['error'][$count];
        $sorted_files[$count]['size']=$files['size'][$count];
        if (!is_uploaded_file($sorted_files[$count]['tmp_name'])) {
            unset($sorted_files[$count]);
        }
        $count++;
    }
    return $sorted_files;
}  
?>

Shows that both methods (with a tweak to the while condition in method 2) work fine, no matter if all fives are uploaded, just 1 or none. Both methods check if a file has been uploaded or not. imho, method 2 is better as it elimiates the need for a loop within a loop, which is something I personally prefer to avoid wherever possible.

Ah, but you’re cheating, because the original function is


function get_da_files($_FILES)

which does raise errors (different errors on different PHP versions). See http://3v4l.org/6g3CU
(The error you get in PHP>=5.4 is the most correct one IMHO)

Off Topic:

If you want to show any and all errors, you can do set error_reporting to -1

that function is pretty nice
But im trying to use froreach loop to go though the array to upload each image but its not working


echo "<pre>";
var_dump($_FILES);
    echo "</pre><hr>";
$da_files=get_files();
echo "<pre>";
var_dump($da_files);
echo "</pre>";

foreach ( $da_files as $key => $val ) {
...
}


shouldn’t that foreach thing be working?
(see attachment)

Im an idiot, fixed the issue by doing thios



foreach ( $da_files as $image ) {

$Name = $image['name'];
$Size = $image['size'];
$Temp = $image['tmp_name'];
$Error = $image['error'];
$Type = $image['type'];
...

Thanks again!

Errrr… you’re welcome? :lol:

The Check destination is writable… Are you on a windows based system? If so, go to your folder where the images are to be uploaded and right click on the folder then select properties and ensure that the “Read Only” box has nothing in it, it will either be ticked or have a coloured box, it needs to be clear of any property and click “Apply” then “Ok”

If you are on a Linux system then the process is a whole new game and its been that long that I have use Linux that I will let someone who knows Linux comment BUT the permissions in Linux for folder permissions is a lot more simpler in PHP with the issue of a permissions change using chmod() function from memory chmod( filename , 0664) where filename is the path to the folder and the number is an Octal value that MUST have a leading zero, the 0664 is read / write for owner and read for everyone else and i believe that files of an executable type can’t be run as they don’t have permission with that setting.