Search code examples
phphtmlmysqlfile-uploadmultiple-file-upload

Multiple file upload -- works fine when all documents are uploaded fails when only few are entered


I have this form in which it is required to upload multiple files(upto 10 files). Here's what the html looks like:

<form action="fileupload.php" method="post" enctype="multipart/form-data">
<tr><td><input type="hidden" name="consultant_id" value="<?php echo $consult_id; ?>"></td></tr>

<tr><td>Offer letter:</td><td> Doc: <input type="file" name="myfile[]"></td></tr>
<tr><td>Project acceptance agreement:</td><td> Doc: <input type="file" name="myfile[]"></td></tr>
<tr><td>Employee book:</td><td> Doc: <input type="file" name="myfile[]"></td></tr>
<tr><td>W4 :</td><td> Doc: <input type="file" name="myfile[]"></td></tr>
<tr><td>State W4 :</td><td> Doc: <input type="file" name="myfile[]"></td></tr>
<tr><td><input type="submit" name="submit" value="Upload">  </td></tr>
</form></table>

I want to upload the files to the server and store their respective paths in database(MySql). Now the following php code works awesome when I give input to all file fields(upload all 10 files) but fails when I want to upload only some files(say 5). Here's the code :

        <?php
    $consultant_id = $_POST['consultant_id'];
    echo $consultant_id;
    $verified_start_date = $_POST['verified_start_date'];

    // Assign valid types
    $valid_mime = array(
        'application/pdf',
        'image/jpeg',
        'image/jpg',



     );

    function upload($files, $dir, $size_limit=1024, $prevent_duplicate=false){
        global $valid_mime;

    // $files must be given.
    if(!isset($files)) return false;

    // Look for $valid_mime array.
    isset($valid_mime) and is_array($valid_mime) or die('Error in data resources, valid_mime array not found.');

    // Make directory if it does not exists. set permission to 0777.
    is_dir($dir) and chmod($dir, 0777) or mkdir($dir, 0777, true);
    //is_dir($consultant_id) and ($dir, 0777) or mkdir($dir/$consultant_id, 0777, true);
    $count = 1;
    foreach($files as $file){
        $file['error'] === UPLOAD_ERR_OK or die('Error in uploading file(s).');

        // Check uploaded-file type.
        in_array($file['type'], $valid_mime) or die();

        // Set size_limit in KB.
        $file['size'] > $size_limit*1024 and die('The uploaded file exceeds the maximum file size.');


        $file_extension = strrchr($file['name'], '.');
        $filename = basename($file['name'], $file_extension);

        $file_path = "{$dir}/{$filename}{$file_extension}";

        // Move uploaded-file from php temp folder to desire one.
        move_uploaded_file($file["tmp_name"], $file_path);

        // Make an array of filepaths
        $output[] = $file_path;
    }

    // Change permission of folder according to security issues.
    chmod($dir, 0755);

    return $output; 
}
/////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////  Controller Section  ////////////////////////////////

// Assign tmp_arr from $_FILES['myfile'] and do die if there is any problem.
$tmp_arr = (isset($_POST['submit']) and isset($_FILES['myfile'])) ? $_FILES['myfile'] : die('Error in posting data.');


// Create an array with desired structure.
for($i=0; $i<count($tmp_arr['name']); $i++){
    $files[] = array(
        'name'      =>  $tmp_arr['name'][$i],
        'type'      =>  $tmp_arr['type'][$i],
        'tmp_name'  =>  $tmp_arr['tmp_name'][$i],
        'error' =>  $tmp_arr['error'][$i],
        'size'      =>  $tmp_arr['size'][$i],
    );
}

// size_limit in KB
$path_arr = upload($files, './public/'.$consultant_id, 1024, true);
?>

I have not mentioned the part where data is entered in database, as I know the problem is somewhere in $tmp_arr['error'][$i] or $file['error'] === UPLOAD_ERR_OK or die('Error in uploading file(s).');

Please take a look.


Solution

  • The problem appears to be that when you have a blank file input, it returns an 'error' value of 4 which means UPLOAD_ERR_NO_FILE. So since the field doesn't match UPLOAD_ERR_OK you immediately stop all code by calling die stopping any files after the first blank from copying over. If the first field was blank, nothing would get get to the move_uploaded_file. If the third out of ten was blank, then the first two files would get copied and when the third was processed it would stop any further files. But you would still just see the error "Error in uploading file(s)."

    Edit:

    <?php
    $consultant_id = $_POST['consultant_id'];
    echo $consultant_id;
    $verified_start_date = $_POST['verified_start_date'];
    
    // Assign valid types
    $valid_mime = array(
        'application/pdf',
        'image/jpeg',
        'image/jpg',
    
    
    
     );
    
    function upload($files, $dir, $size_limit=1024, $prevent_duplicate=false){
        global $valid_mime;
    
        // $files must be given.
        if(!isset($files)) return false;
    
        //please use proper if statements. This is confusing.
        //isset($valid_mime) and is_array($valid_mime) or die('Error in data resources, valid_mime array not found.');
    
        // Look for $valid_mime array.
        if(!isset($valid_mime) || !is_array($valid_mime)) {
            die('Error in data resources, valid_mime array not found.');
        }
    
        //please use proper if statements. This is confusing.
        // is_dir($dir) and chmod($dir, 0777) or mkdir($dir, 0777, true);
    
        // Make directory if it does not exists. set permission to 0777.
        if(!is_dir($dir)) {
            mkdir($dir, 0777, true);
        } else {
            //try to chmod if the directory does exist
            chmod($dir, 0777);
        }
    
        if(!is_writable($dir)){
            die('Error unable to write to specified directory.');
        }
    
        foreach($files as $key=>$file){
            //switch case on the error code
            //you can find a list of these error codes at: http://php.net/manual/en/features.file-upload.errors.php
            switch($file['error']){
                default:
                    //triggered if an unknown error happened. Newer php versions might bring new constants.
                    //log error for this file
                    $output[$key] = array('error'=>true, 'message'=>'An unknown error occurred');
                    break;
                case UPLOAD_ERR_INI_SIZE:
                    //log error for this file
                    $output[$key] = array('error'=>true, 'message'=>'File size exceeds ini setting');
                    break;
                case UPLOAD_ERR_FORM_SIZE:
                    //log error for this file
                    $output[$key] = array('error'=>true, 'message'=>'File size exceeds MAX_FILE_SIZE setting');
                    break;
                case UPLOAD_ERR_PARTIAL:
                    //log error for this file
                    $output[$key] = array('error'=>true, 'message'=>'File was only partially uploaded');
                    break;
                case UPLOAD_ERR_NO_FILE:
                    //log error for this file
                    $output[$key] = array('error'=>true, 'message'=>'File input was blank');
                    break;
                case UPLOAD_ERR_NO_TMP_DIR:
                    //log error for this file
                    $output[$key] = array('error'=>true, 'message'=>'Missing temporary folder');
                    break;
                case UPLOAD_ERR_CANT_WRITE:
                    //log error for this file
                    $output[$key] = array('error'=>true, 'message'=>'Failed to write file to disk');
                    break;
                case UPLOAD_ERR_OK:
                    //upload worked fine
                    // Check uploaded-file type.
                    if(in_array($file['type'], $valid_mime)){
                        // Set size_limit in KB.
                        if($file['size'] <= $size_limit*1024){
                            //get the file extension.
                            $file_extension = strrchr($file['name'], '.');
                            //get the filename
                            $filename = basename($file['name'], $file_extension);
                            //full filename and path
                            $file_path = "{$dir}/{$filename}{$file_extension}";
    
                            // Move uploaded-file from php temp folder to desire one.
                            // function returns true if the move was successful
                            if(move_uploaded_file($file["tmp_name"], $file_path)){
                                $output[$key] = array('error'=>false, 'message'=>'File was uploaded successfully', 'file'=>$file_path);
                            } else {
                                $output[$key] = array('error'=>true, 'message'=>'Unspecified error when moving the uploaded file');
                            }
                        } else {
                            //log error for this file
                            $output[$key] = array('error'=>true, 'message'=>'The uploaded file exceeds the maximum file size');
                        }
                    } else {
                        //log error for this file
                        $output[$key] = array('error'=>true, 'message'=>'Failed to write file to disk');
                    }
            }
        }
    
        // Change permission of folder according to security issues.
        chmod($dir, 0755);
    
        return $output; 
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////  Controller Section  ////////////////////////////////
    
    // Assign tmp_arr from $_FILES['myfile'] and do die if there is any problem.
    $tmp_arr = (isset($_POST['submit']) and isset($_FILES['myfile'])) ? $_FILES['myfile'] : die('Error in posting data.');
    
    
    // Create an array with desired structure.
    for($i=0; $i<count($tmp_arr['name']); $i++){
        $files[] = array(
            'name'      =>  $tmp_arr['name'][$i],
            'type'      =>  $tmp_arr['type'][$i],
            'tmp_name'  =>  $tmp_arr['tmp_name'][$i],
            'error' =>  $tmp_arr['error'][$i],
            'size'      =>  $tmp_arr['size'][$i],
        );
    }
    
    // size_limit in KB
    $path_arr = upload($files, './public/'.$consultant_id, 1024, true);
    

    If I were doing this, this is the function I would use. If one uploaded file has a problem, it won't stop the remaining files from uploading and translates the 'error' key from $_FILES into a more user friendly message. You could easily loop over the return array and check the boolean 'error' key to see if a specific file has a problem. If 'error' is true, you could echo the message for the user to see. If there was no error, the message 'File was uploaded successfully' can be displayed. The keys from the return array correspond to the same keys found in the array passed in. So $file[0] is the same file as $returnResult[0] for easy reference.