Step 13 – Uploading multiple files (images) in CodeIgniter

This tutorial turned into a Github repository.
Please give it a try at:
https://github.com/avenirer/MY_Upload

(created at: December 15, 2014; last update: December 18, 2014)
Among the misunderstandings when comes to CodeIgniter are related to multiple file (image) uploading and image manipulation. So I thought I’d give it a try (with the help of a reader’s request). I can’t stress out enough that this is not just a copy/paste code. So, for all the copy-paster people in the world, please, read before talking.

First draft

Being a testing controller, I will only use an upload field. First of all, let’s create the controller that will load the view with the form. I chose to work with index() method inside the Welcome class:

<?php defined('BASEPATH') OR exit('No direct script access allowed');

class Welcome extends CI_Controller {

  public function index()
  {
    $this->load->helper('form');
    $data = array();
    $data['title'] = 'Multiple file upload';
    $this->load->view('upload_form',$data);
  }
}

Now, after we’ve done the basic controller, we need to create the form that will help users upload multiple files. As we are going to have a form, it would be a good idea to load the form helper. So, create upload_form.php inside the views folder:

<!DOCTYPE html>
<html>
  <head>
    <title><?php echo $title; ?></title>
  </head>
  <body>
    <h1>Upload multiple files</h1>
    <?php echo form_open_multipart();?>
      <p>Upload file(s):</p>
      <?php echo form_upload('uploadedimages[]','','multiple'); ?>
      <br />
      <br />
      <?php echo form_submit('submit','Upload');?>
    <?php echo form_close();?>
  </body>
</html>

Being a form that will have a file input field, it has to have a multipart attribute. As you can see, we’ve added no parameters to form_open_multipart, because we want the form to be processed by the same controller that outputs it.

The most important part about the form is the file input field. To upload multiple files, we need to put as attributes for the input field the “multiple” attribute. Also, when talking about multiple values for a field, is important that the name of the input field to have square brackets at the end “fieldname[]“.

So echo form_upload(‘uploadedimages[]’,”,’multiple’);  will output as: <input type=”file” name=”uploadedimages[]” multiple />

Now let’s return to our controller and see what will happen if we load two images (I will advise you to have two or more images for testing). For this, we will change it to first verify if there was a form submission. If there was a form submission we will output the uploaded files’ properties to see if the upload took place. Else, we will output the form:

public function index()
{
  $this->load->helper('form');
  $data = array();
  $data['title'] = 'Multiple file upload';
  if($this->input->post())
  {
    echo '<pre>';
    print_r($_FILES);
    echo '</pre>';
  }
  else
  {
    $this->load->view('upload_form', $data);
  }
}

Now, to test it, let’s visit http://localhost (or whatever controller and method you chose), and upload two or more files in the form. If you submit the form you will be able to see how the $_FILES work. Take a good look at the array that is passed as $_FILES:

Of course the values may differ, but the principle is the same in all situations involving $_FILES.

Now, before processing the files, let’s see what we want to do. We will first upload the original files in upload directory, and then we will work with them. So, let’s create a directory named upload in the public area of the server (where we also keep the css and javascripts). If you have XAMPP, that will be in YOUR_XAMPP_DIRECTORY/htdocs/upload. Make sure that upload directory is writable.

After that, we get back to our controller and make a few changes. First of all, we have to do a small hack so that we can use the upload library with more than one file:

public function index()
{
  $this->load->helper('form');
  $data = array();
  $data['title'] = 'Multiple file upload';

  if($this->input->post())
  {
    // retrieve the number of images uploaded;
    $number_of_files = sizeof($_FILES['uploadedimages']['tmp_name']);
    // considering that do_upload() accepts single files, we will have to do a small hack so that we can upload multiple files. For this we will have to keep the data of uploaded files in a variable, and redo the $_FILE.
    $files = $_FILES['uploadedimages'];
    $errors = array();

    // first make sure that there is no error in uploading the files
    for($i=0;$i<$number_of_files;$i++)
    {
      if($_FILES['uploadedimages']['error'][$i] != 0) $errors[$i][] = 'Couldn\'t upload file '.$_FILES['uploadedimages']['name'][$i];
    }
    if(sizeof($errors)==0)
    {
      // now, taking into account that there can be more than one file, for each file we will have to do the upload
      // we first load the upload library
      $this->load->library('upload');
      // next we pass the upload path for the images
      $config['upload_path'] = FCPATH . 'upload/';
      // also, we make sure we allow only certain type of images
      $config['allowed_types'] = 'gif|jpg|png';
      for ($i = 0; $i < $number_of_files; $i++) {
        $_FILES['uploadedimage']['name'] = $files['name'][$i];
        $_FILES['uploadedimage']['type'] = $files['type'][$i];
        $_FILES['uploadedimage']['tmp_name'] = $files['tmp_name'][$i];
        $_FILES['uploadedimage']['error'] = $files['error'][$i];
        $_FILES['uploadedimage']['size'] = $files['size'][$i];
        //now we initialize the upload library
        $this->upload->initialize($config);
        // we retrieve the number of files that were uploaded
        if ($this->upload->do_upload('uploadedimage'))
        {
          $data['uploads'][$i] = $this->upload->data();
        }
        else
        {
          $data['upload_errors'][$i] = $this->upload->display_errors();
        }
      }
    }
    else
    {
      print_r($errors);
    }
    echo '<pre>';
    print_r($data);
    echo '</pre>';
  }
  else
  {
    $this->load->view('upload_form', $data);
  }
}

If now we’re testing the upload form with image files and/or other type of files, we will either receive an array with the uploaded images’ data or an array with errors.

Cleaning up the code

Now that looks nice, but it doesn’t look too clean. What if the form will have more than just upload field? It will all look too messy. So let’s try in put all the upload inside a validation callback and make a method that will be the callback. Also, considering that the callback will also return the upload data, we should make sure that we will use a variable that is available across the entire controller and not just in the callback function. So just before we start the methods, we should define an _uploaded variable: private $_uploaded = array();

That means that the class would look like this now:

<?php defined('BASEPATH') OR exit('No direct script access allowed');

class Welcome extends CI_Controller {

  private $_uploaded;

  public function index()
  {
    $this->load->helper('form');
    $data['title'] = 'Multiple file upload';
    
    // let's consider that the form would come with more fields than just the files to be uploaded. If this is the case, we would need to do some sort of validation. If we are talking about images, the only method of validation for us would be to put the upload process inside a validation callback;
    $this->load->library('form_validation');

    //now we set a callback as rule for the upload field
    $this->form_validation->set_rules('uploadedimages[]','Upload image','callback_fileupload_check');
    
    //was something posted?
    if($this->input->post())
    {

      //run the validation
      if($this->form_validation->run())
      {
        // for now let's just verify if all went ok with the upload...
        echo '<pre>';
        print_r($this->_uploaded);
        echo '</pre>';
        // from here on you can do whatever you wish with the uploaded data or the other form fields that you might have. I decided to exit here, since this is not the object of our tutorial.
        exit;
      }
    }
    $this->load->view('upload_form', $data);
  }

  // now the callback validation that deals with the upload of files
  public function fileupload_check()
  {
    
    // we retrieve the number of files that were uploaded
    $number_of_files = sizeof($_FILES['uploadedimages']['tmp_name']);

    // considering that do_upload() accepts single files, we will have to do a small hack so that we can upload multiple files. For this we will have to keep the data of uploaded files in a variable, and redo the $_FILE.
    $files = $_FILES['uploadedimages'];

    // first make sure that there is no error in uploading the files
    for($i=0;$i<$number_of_files;$i++)
    {
      if($_FILES['uploadedimages']['error'][$i] != 0)
      {
        // save the error message and return false, the validation of uploaded files failed
        $this->form_validation->set_message('fileupload_check', 'Couldn\'t upload the file(s)');
        return FALSE;
      }
    }
    
    // we first load the upload library
    $this->load->library('upload');
    // next we pass the upload path for the images
    $config['upload_path'] = FCPATH . 'upload/';
    // also, we make sure we allow only certain type of images
    $config['allowed_types'] = 'gif|jpg|png';

    // now, taking into account that there can be more than one file, for each file we will have to do the upload
    for ($i = 0; $i < $number_of_files; $i++)
    {
      $_FILES['uploadedimage']['name'] = $files['name'][$i];
      $_FILES['uploadedimage']['type'] = $files['type'][$i];
      $_FILES['uploadedimage']['tmp_name'] = $files['tmp_name'][$i];
      $_FILES['uploadedimage']['error'] = $files['error'][$i];
      $_FILES['uploadedimage']['size'] = $files['size'][$i];
      
      //now we initialize the upload library
      $this->upload->initialize($config);
      if ($this->upload->do_upload('uploadedimage'))
      {
        $this->_uploaded[$i] = $this->upload->data();
      }
      else
      {
        $this->form_validation->set_message('fileupload_check', $this->upload->display_errors());
        return FALSE;
      }
    }
    return TRUE;
  }
}

I sure hope I didn’t loose you there… I only moved the uploading part inside a callback function for cleaner and easier to understand code.

Now, we just have to make sure that we echo the errors that come from the callback function. For this, we return to the upload_form view and do an echo of the eventual errors:

<!DOCTYPE html>
<html>
  <head>
    <title><?php echo $title; ?></title>
  </head>
  <body>
    <h1>Upload multiple files</h1>
    <?php echo form_open_multipart();?>
      <p>Upload file(s):</p>
      <?php echo form_error('uploadedimages[]'); ?>
      <?php echo form_upload('uploadedimages[]','','multiple'); ?>
      <br />
      <br />
      <?php echo form_submit('submit','Upload');?>
    <?php echo form_close();?>
  </body>
</html>

 

Now, if we are trying to upload a file that is not an image (or doesn’t respect your conditions set in the configuration of the upload libary), we should get an error and be returned to the form. Instead, if we upload images, we should be greeted with the upload data.

With these in hand, you can work with the image library. But this will be the subject of another tutorial. (I sure hope you don’t mind about this Rodge, but this is a big subject so I have to split it in two…)

26 comments

  1. Thank you very much for this wonderful tutorial. I really appreciate your effort.
    Again, you gave me additional knowledge that I can use to my project and future projects.
    I am going to start another adventure using this tutorial and I hope you create more tutorials that may help other beginner like me.
    I wish you more power and lucks.
    Thank you very much!

  2. Thank you from the tutorial,

    I have just one question, is it possible in codeigniter to have more than one browse for image button in a single form to save all in one table in a database.I have two text fields and I want different images for each one. I want to save one or more than one images with 1st text box and one or more than one images with the second text box in single form.

    When i want to retrieve these images and text fields all should be load with a page slug.

    I am thanking you in advanced!

    1. Never tried that, but in principle you could insert more than one file input element in the same form and you just work with those input elements as usual.

  3. Thank you for this, you probably saved me a lot of time 🙂

    I wish all tutorials were this thorough and concise!

  4. Hello, how can I rename picture with callback processing before getting my lastinsert(id)
    I’d like to rename with the lastinsert(id) and a number like this 234-1.jpeg,234-2.jpeg,234-3.jpeg for exemple with 3 pictures.
    Thanks

    1. Well… you can’t create a file name without having the name you need. As you can see, I’ve uploaded the files in a validation callback (as the simple upload of the files being the validation itself). I guess that you want the images to be loaded with a post or something, and you want to name the images using the post ID. But you can only retrieve a last insert id after the post was submitted and validated, validation being already done with the upload of the image files. Even if you won’t be able to name the images inside the callback validation method, you can at any time work with the data provided by the method by using the $this->_uploaded array, which tells you everything about what was uploaded, including the name of the files and their location. Let’s suppose you’ve retrieved the last insert id when you inserted the post into the table (we will store it in a variable called $lastinsertid). If you have a name and a location of the image files, you simply rename them by iterating through the array:

      foreach($this->_uploaded as $key => $file)
      {
      rename($file['full_path'], $file['file_path'].$lastinsertid.'-'.$key);
      }

      Hope this helps you.

  5. Nice tutorial. I’m wonderying if it’s possibile to rename uploaded image. One more question: si it possible to use CI image upload feature with tre Drpbox librari?

  6. Ops..sorry, I didn’t realize you already provided info about renaming file. Also i Washington wrong writing Dropbox….I meant Dropzone. Sorry

    1. Well, I don’t think there’s a problem using Dropzone, as that is only a front facing matter and not affecting what is uploaded.

  7. Excellent tutorials. I have one question though. I downloaded your Github repository. I just wanted to know if you could either explain how to do this in a tutorial, or email me to share your ideas about it.

    I want to make a relation to the permissions from groups to menu & page items, so when I add a new menu item or a new page, or for when I am editing an existing one, that the permissions can be set to which groups can view that content, perhaps via a check-box, or a drop-down menu. If a user does not have the appropriate permissions to view the content/menu item, then they are redirected to the Index method of the main frontend controller when they try to access it.

    Also, how easy would it be to link a menu item to an existing page using the pages slug? If I can figure out how to link to permissions to menu items/pages. And link a menu item to a page instead of a parent item or a slug, I would be most appreciative for your help. Your tutorials are amazing my friend, they truly are.

      1. Yes indeed it is. how much do ya’ charge? 😛

        Also, I would not expect the work to be done for me lol. But I would just like to know how you might start to go about this type of thing. 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

No spam? * Time limit is exhausted. Please reload CAPTCHA.