phpcodeigniter-3ajaxform

Codeigniter - Not able to upload files from multiple file inputs


I have a multi-steps form which has many input fields including multiple file inputs and some of them are dynamically generated with the JavaScript.In one section of the form, users can upload multiple files. I am able to insert the form data into the database but not able to upload those multiple documents files which users upload.I am using AJAX to upload form data and files. This is my controller code,

<?php

defined('BASEPATH') or exit('No direct script access allowed');

class Employee extends MY_Controller
{

    public function index()
    {
        $data['title'] = 'employees';
        $this->view('employees/view_employee_view', $data);
    }

    public function save()
    {
        $req = $this->input->post();

            // Some other code here

            // First upload the profile picture
            if (!empty($_FILES['emp_photo']) AND $_FILES["emp_photo"]["error"] == 0) {

                // Upload employee photo
                $emp_photo_data = $this->upload_file(EMP_PHOTOS_FOLDER, 'emp_photo');

                if (array_key_exists('error', $emp_photo_data)) {
                    $this->response(['success'=>false, 'msg' => '<b>File upload failed!Try again</b>', 'data'=> ''], 500);
                    exit();
                }
                else {
                    $employee['emp_photo'] = $emp_photo_data['file_name'];
                }
            }
            
            $this->db->trans_start();

            $emp_id = $this->Employee_model->save($employee);

            // Save edu details of employee
            $this->add_edu_details($emp_id);

            // Upload employee documents
            $doc_details = $this->upload_emp_docs($emp_id); //<-- Here is the problem
            
            if (is_string($doc_details)) {
                // it's an error
                $this->response(['success' => false, 'msg' => '<b>Employee documents upload failed!</b>', 'data' => $doc_details]);
                $this->db->trans_rollback(); 
                exit();
            }
            else{
                if ($doc_details === FALSE) {
                    // Error occured - rollback
                    $this->response(['success' => false, 'msg' => '<b>Employee documents upload failed!</b>', 'data' => '']);
                    $this->db->trans_rollback();
                    exit();
                }
            }

    }


    private function upload_emp_docs($emp_id)
    {
        $counts = $this->input->post('emp_docs_count');

        $req = $this->input->post();
        
        $data = [];
        for ($i = 1; $i <= $counts; $i++) { 
            
            if (!empty($_FILES['emp_doc_' . $i])) {
                $res = $this->upload_file(EMP_DOCS_FOLDER, 'emp_doc_' . $i, 'pdf');
                $done = move_uploaded_file($_FILES["emp_doc_" . $i]["tmp_name"], EMP_PHOTOS_FOLDER . md5(time()) . '.jpg');
                if (array_key_exists('error', $res)) {
                    return $res['error']; // return error string
                }
                else {
                    $data[$i] = ["emp_id" => $emp_id, 'file_name' => $res["file_name"], 'doc_type'=> $req["emp_doc_type_{$i}"] ];
                }
            }
        }

        if (!empty($data)) {
            $done = $this->Emp_doc_model->save($data);
            return !empty($done) ? TRUE : FALSE;
        }
        else {
            return FALSE;
        }
    }

}

Here is the code in MY_Controller.php which actually do the uploading task

public function upload_file($dir, $input, $allowed_types = 'jpeg|jpg|png')
    {
        $config['upload_path']   = $dir;
        $config['allowed_types'] = !empty($allowed_types) ? $allowed_types : 'jpeg|jpg|png';
        $config['max_size']      = 0;
        $config['max_width']     = 0;
        $config['max_height']    = 0;
        $config['file_name']     = md5(time() . rand(1, 1000));

        $this->load->library('upload', $config);

        if (!$this->upload->do_upload($input)) {
            return array('error' => $this->upload->display_errors());
        } else {
            $data = $this->upload->data();

            // If it is image then resize it to reduce the image file size
            if ($data['is_image']) {
                $this->resize_image($data['full_path']);
            }

            return $data;
        }
    }

In one section a user can select a profile picture and I am able to upload that file using the above code but I am not able to find why it is not working when I try to upload multiple files.

This is the HTML code for that section

<div class="col upload-area">
<div class="input-group">
   <a class="btn bg-grey btn-block" id="add-file-group">ADD FILE</a>
</div>
<input type="hidden" id="emp_docs_count" name="emp_docs_count" value="1">
<div class="row clearfix file-upload-group">
   <div class="col-lg-4 col-md-4 col-sm-4 col-xs-6">
      <div class="form-group">
         <div class="form-line">
            <input name="emp_doc_1" type="file" class="form-control">
         </div>
      </div>
   </div>
   <div class="col-lg-4 col-md-4 col-sm-4 col-xs-6">
      <div class="form-group">
         <div class="form-line">
            <select name="emp_doc_type_1" id="emp_doc_type_1" class="form-control show-tick selectpicker" title="Choose one of the following...">
               <option value="<?= OFFER_LETTER ?>"><?= OFFER_LETTER ?></option>
               <option value="<?= APPOINT_LETTER ?>"><?= APPOINT_LETTER ?></option>
               <option value="<?= CONF_LETTER ?>"><?= CONF_LETTER ?></option>
               <option value="<?= APP_LETTER_1 ?>"><?= APP_LETTER_1 ?></option>
               <option value="<?= APP_LETTER_2 ?>"><?= APP_LETTER_2 ?></option>
               <option value="<?= REL_LETTER ?>"><?= REL_LETTER ?></option>
               <option value="<?= EXP_TER_LETTER ?>"><?= EXP_TER_LETTER ?></option>
            </select>
         </div>
      </div>
   </div>
   <div class="col-lg-4 col-md-4 col-sm-4 col-xs-12">
      <!-- <button type="button" class="btn btn-primary btn-sm  waves-effect"><i class="material-icons">cloud_upload</i></button> -->
      <button type="button" class="btn btn-danger btn-sm  waves-effect remove-me"><i class="material-icons">delete_forever</i></button>
   </div>
</div>

This is the JS code to generate input tags dyanmically

$("#add-file-group").click(function() {
    // Dynamically generate the id for upload-group elements
    const count = $(".upload-area").find(".file-upload-group").length + 1;

    const newID = "upload-area-id-" + count;

    const fileUploadGroup = `<div class="row clearfix file-upload-group">
    <div class="col-lg-4 col-md-4 col-sm-4 col-xs-6">
        <div class="form-group">
            <div class="form-line">
                <input type="file" class="form-control" id="file_${count}" name="emp_doc_${count}">
            </div>
        </div>
    </div>
    <div class="col-lg-4 col-md-4 col-sm-4 col-xs-6">
        <div class="form-group">
            <div class="form-line">
            <select name="emp_doc_type_${count}" id="emp_doc_type_${count}" data-size="2" class="form-control show-tick selectpicker" title="Choose one of the following..." tabindex="-98"><option class="bs-title-option" value="">Choose one of the following...</option>
                <option value="OFFER LETTER">OFFER LETTER</option>
                <option value="APPOINTEMENT LETTER">APPOINTEMENT LETTER</option>
                <option value="CONFIRMATION LETTER">CONFIRMATION LETTER</option>
                <option value="APPRAISAL LETTER 1">APPRAISAL LETTER 1</option>
                <option value="APPRAISAL LETTER 2">APPRAISAL LETTER 2</option>
                <option value="RELIVING LETTER">RELIVING LETTER</option>
                <option value="EXPERIENCE / TERMINATION LETTER">EXPERIENCE / TERMINATION LETTER</option>
            </select>
            </div>
        </div>
    </div>
    <div class="col-lg-4 col-md-4 col-sm-4 col-xs-12">
        <button type="button" class="btn btn-primary btn-sm  waves-effect"><i class="material-icons">cloud_upload</i></button>
        <button type="button" class="btn btn-danger btn-sm  waves-effect remove-me"><i class="material-icons">delete_forever</i></button>
        
    </div>
</div>`;

    $(".upload-area").append(fileUploadGroup);

    // Increment the doc counter of hidden field
    $('#emp_docs_count').val(count);

    // Refresh the selectpicker to work on this generated element
    $(".selectpicker").selectpicker("refresh");
});
                                    

Solution

  • I have finally solved it, I just need to initialize upload library every time i call the upload_file() method, I have just added one line and it start working,

    public function upload_file($dir, $input, $allowed_types = 'jpeg|jpg|png')
        {
            $config['upload_path']   = $dir;
            $config['allowed_types'] = !empty($allowed_types) ? $allowed_types : 'jpeg|jpg|png';
            $config['max_size']      = 0;
            $config['max_width']     = 0;
            $config['max_height']    = 0;
            $config['file_name']     = md5(time() . rand(1, 1000));
    
            $this->load->library('upload');
    
            $this->upload->initialize($config); //<-- this line solved it
    
            if (!$this->upload->do_upload($input)) {
                return array('error' => $this->upload->display_errors());
            } else {
                $data = $this->upload->data();
    
                // If it is image then resize it to reduce the image file size
                if ($data['is_image']) {
                    $this->resize_image($data['full_path']);
                }
    
                return $data;
            }
        }