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); } }
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…)
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!
Thank for for taking the time to read my tutorials.
Hello, I’ve just put the sequel to this tutorial, as you’ve asked not only for a tutorial about multiple image upload but also for image manipulation: Crop and resize uploaded images in CodeIgniter
Nice tutorial regarding multiple file uploads. but, could you also please cover, how to customize file upload error message(s). i have posted a question about it under http://forum.codeigniter.com/thread-61531.html codeigniter forum.
i hope you could help. Thanks!
You won’t believe it, but I’ve just wrote you on the forum before you commenting here :))
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!
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.
Thank you for this, you probably saved me a lot of time 🙂
I wish all tutorials were this thorough and concise!
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
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.
Very simple, it works fine.
Thanks so much.
You’re welcome.
That work fine with a normal upload. But how can i upload them into database???
You want to upload the file inside a database? For this you only need to know how the models work…
Can u give me some of tutorial?
Thank u very much..
it’s working wonderful………..
Thank you. You can also try the upload library I’ve put on Github: https://github.com/avenirer/MY_Upload
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?
Ops..sorry, I didn’t realize you already provided info about renaming file. Also i Washington wrong writing Dropbox….I meant Dropzone. Sorry
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.
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.
Well… this is a subject that I could very well be paid for… 🙂
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. 🙂
Thanks a lot
make it fun
Thanks for this wonderful tutorial..keep it up……..