How to Upload Files with CodeIgniter and AJAX

How to Upload Files with CodeIgniter and AJAX

Tutorial Details
  • Program: CodeIgniter, jQuery, AjaxFileUpload
  • Difficulty: Intermediate
  • Estimated Completion Time: 45-60 Minutes

Uploading files asnychronously can be a pain at the best of times, but when coupled with CodeIgniter, it can be a particularly frustrating experience. I finally found a way that not only works consistently, but keeps to the MVC pattern. Read on to find out how!


Preface

In this tutorial, we’ll be using the PHP framework CodeIgniter, the JavaScript framework jQuery, and the script AjaxFileUpload.

It’s assumed you have a working knowledge of CodeIgniter and jQuery, but no prior knowledge of AjaxFileUpload is necessary. It is also assumed that you already have an install of CodeIgniter already set up.

For the sake of brevity, the loading in of certain libraries/models/helpers has been omitted. These can be found in the source code supplied, and is pretty standard stuff.

You’ll also need a database, and a table, called files. The SQL to create said table is:

CREATE TABLE `files` (
  `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `filename` varchar(255) NOT NULL,
  `title` varchar(100) NOT NULL
);

By the end of the tutorial, your file structure should look similar to this (omitting unchaged folders/files):

public_html/
– application/
—- controllers/
—— upload.php
—- models/
—— files_model.php
—- views/
—— upload.php
—— files.php
– css/
—- style.css
– files/
– js/
—- AjaxFileUpload.js
—- site.js


Step 1 - Creating the Form

Set up the Controller

First, we need to create our upload form. Create a new Controller, called upload, and in the index method, render the view upload.

Your controller should look like this:

class Upload extends CI_Controller 
{
	public function __construct()
	{
		parent::__construct();
		$this->load->model('files_model');
		$this->load->database();
		$this->load->helper('url');
	}

	public function index()
	{
		$this->load->view('upload');
	}
}

We are also loading in the files model, so we can use it in our methods. A better alternative may be to autoload it in your actual project.

Create the Form

Create your view, upload.php. This view will contain our upload form.

<!doctype html>
<html>
<head>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
	<script src="<?php echo base_url()?>js/site.js"></script>
	<script src="<?php echo base_url()?>js/ajaxfileupload.js"></script>
	<link href="<?php echo base_url()?>css/style.css" rel="stylesheet" />
</head>
<body>
	<h1>Upload File</h1>
	<form method="post" action="" id="upload_file">
		<label for="title">Title</label>
		<input type="text" name="title" id="title" value="" />

		<label for="userfile">File</label>
		<input type="file" name="userfile" id="userfile" size="20" />

		<input type="submit" name="submit" id="submit" />
	</form>
	<h2>Files</h2>
	<div id="files"></div>
</body>
</html>

Don’t forget to place ajaxfileupload.js in js/.

As you can see, we are loading in our scripts at the top; jQuery, AjaxFileUpload, and our own js file. This will house our custom JavaScript.

Then, we are simply creating a standard HTML form. The empty #files div is where our list of uploaded files will be.

Some Simple CSS

Just so it doesn’t look quite so bad, lets add some basic CSS to our file style.css in css/.

h1, h2 { font-family: Arial, sans-serif; font-size: 25px; }
h2 { font-size: 20px; }

label { font-family: Verdana, sans-serif; font-size: 12px; display: block; }
input { padding: 3px 5px; width: 250px; margin: 0 0 10px; }
input[type="file"] { padding-left: 0; }
input[type="submit"] { width: auto; }

#files { font-family: Verdana, sans-serif; font-size: 11px; }
#files strong { font-size: 13px; }
#files a { float: right; margin: 0 0 5px 10px; }
#files ul {	list-style: none; padding-left: 0; }
#files li { width: 280px; font-size: 12px; padding: 5px 0; border-bottom: 1px solid #CCC; }

Step 2 - The Javascript

Create and open site.js in js/. Place the following code:

$(function() {
	$('#upload_file').submit(function(e) {
		e.preventDefault();
		$.ajaxFileUpload({
			url 			:'./upload/upload_file/', 
			secureuri		:false,
			fileElementId	:'userfile',
			dataType		: 'json',
			data			: {
				'title'				: $('#title').val()
			},
			success	: function (data, status)
			{
				if(data.status != 'error')
				{
					$('#files').html('<p>Reloading files...</p>');
					refresh_files();
					$('#title').val('');
				}
				alert(data.msg);
			}
		});
		return false;
	});
});

The JavaScript hijacks the form submit and AjaxFileUpload takes over. In the background, it creates an iframe and submits the data via that.

We’re passing across the title value in the data parameter of the AJAX call. If you had any more fields in the form, you’d pass them here.

We then check our return (which will be in JSON). If no error occured, we refresh the file list (see below), clear the title field. Regardless, we alert the response message.


Step 3 - Uploading the File

The Controller

Now on to uploading the file. The URL we are uploading to is /upload/upload_file/, so create a new method in the upload controller, and place the following code in it.

public function upload_file()
{
	$status = "";
	$msg = "";
	$file_element_name = 'userfile';
	
	if (empty($_POST['title']))
	{
		$status = "error";
		$msg = "Please enter a title";
	}
	
	if ($status != "error")
	{
		$config['upload_path'] = './files/';
		$config['allowed_types'] = 'gif|jpg|png|doc|txt';
		$config['max_size']	= 1024 * 8;
		$config['encrypt_name'] = TRUE;

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

		if (!$this->upload->do_upload($file_element_name))
		{
			$status = 'error';
			$msg = $this->upload->display_errors('', '');
		}
		else
		{
			$data = $this->upload->data();
			$file_id = $this->files_model->insert_file($data['file_name'], $_POST['title']);
			if($file_id)
			{
				$status = "success";
				$msg = "File successfully uploaded";
			}
			else
			{
				unlink($data['full_path']);
				$status = "error";
				$msg = "Something went wrong when saving the file, please try again.";
			}
		}
		@unlink($_FILES[$file_element_name]);
	}
	echo json_encode(array('status' => $status, 'msg' => $msg));
}

This code loads in the CodeIgniter upload library with a custom config. For a full reference of it, check out the CodeIgniter docs.

We do a simple check to determine if the title is empty or not. If it isn’t, we load in the CodeIgniter upload library. This library handles a lot of our file validation for us.

Next, we attempt to upload the file. if successful, we save the title and the filename (passed in via the returned data array).

Remember to delete the temp file off the server, and echo out the JSON so we know what happened.

The Model

In keeping with the MVC pattern, our DB interaction will be handled by a model.

Create files_model.php, and add the following code:

class Files_Model extends CI_Model {

	public function insert_file($filename, $title)
	{
		$data = array(
			'filename'		=> $filename,
			'title'			=> $title
		);
		$this->db->insert('files', $data);
		return $this->db->insert_id();
	}

}

Files Folder

We should also create the folder our files will be uploaded to. Create new file in your web root called files, making sure it is writable by the server.


Step 4 - The File List

Upon a successful upload, we need to refresh the files list to display the change.

The JavaScript

Open site.js and add the following code to the bottom of the file, below everything else.

function refresh_files()
{
	$.get('./upload/files/')
	.success(function (data){
		$('#files').html(data);
	});
}

This simply calls a url and inserts the returned data into a div with an id of files.

We need to call this function on the page load to initially show the file list. Add this in the document ready function at the top of the file:

refresh_files();

The Controller

The URL we are calling to get the file list is /upload/files/, so create a new method called files, and place in the following code:

public function files()
{
	$files = $this->files_model->get_files();
	$this->load->view('files', array('files' => $files));
}

Quite a small method, we use our model to load in the currently saved files and pass it off to a view.

The Model

Our model handles the retrieval of the file list. Open up files_model.php, and add in the get_files() function.

public function get_files()
{
	return $this->db->select()
			->from('files')
			->get()
			->result();
}

Quite simple really: select all the files stored in the database.

The View

We need to create a view to display the list of files. Create a new file, called files.php, and paste in the following code:

<?php
if (isset($files) && count($files))
{
	?>
		<ul>
			<?php
			foreach ($files as $file)
			{
				?>
				<li class="image_wrap">
					<a href="#" class="delete_file_link" data-file_id="<?php echo $file->id?>">Delete</a>
					<strong><?php echo $file->title?></strong>
					<br />
					<?php echo $file->filename?>
				</li>
				<?php
			}
			?>
		</ul>
	</form>
	<?php
}
else
{
	?>
	<p>No Files Uploaded</p>
	<?php
}
?>

This loops through the files and displays the title and filename of each. We also display a delete link, which include a data attribute of the file ID.


Deleting the File

To round off the tutorial, we’ll add in the functionality to delete the file, also using AJAX.

The JavaScript

Add the following in the document ready function:

$('.delete_file_link').live('click', function(e) {
	e.preventDefault();
	if (confirm('Are you sure you want to delete this file?'))
	{
		var link = $(this);
		$.ajax({
			url			: './upload/delete_file/' + link.data('file_id'),
			dataType	: 'json',
			success		: function (data)
			{
				files = $(#files);
				if (data.status === "success")
				{
					link.parents('li').fadeOut('fast', function() {
						$(this).remove();
						if (files.find('li').length == 0)
						{
							files.html('<p>No Files Uploaded</p>');
						}
					});
				}
				else
				{
					alert(data.msg);
				}
			}
		});
	}
});

It’s always a good idea to get a user confirmation when deleting information.

When a delete link is clicked, we display a confirm box asking if the user is sure. If they are, we make a call to /upload/delete_file, and if successful, we fade it from the list.

The Controller

Like above, the url we are calling is /upload/delete_file/, so create the method delete_file, and add the following code:

public function delete_file($file_id)
{
	if ($this->files_model->delete_file($file_id))
	{
		$status = 'success';
		$msg = 'File successfully deleted';
	}
	else
	{
		$status = 'error';
		$msg = 'Something went wrong when deleteing the file, please try again';
	}
	echo json_encode(array('status' => $status, 'msg' => $msg));
}

Again, we let the model do the heavy lifting, echoing out the output.

The Model

We’re now at the final piece of the puzzle: our last two methods.

public function delete_file($file_id)
{
	$file = $this->get_file($file_id);
	if (!$this->db->where('id', $file_id)->delete('files'))
	{
		return FALSE;
	}
	unlink('./files/' . $file->filename);	
	return TRUE;
}

public function get_file($file_id)
{
	return $this->db->select()
			->from('files')
			->where('id', $file_id)
			->get()
			->row();
}

Because we only pass the ID, we need to get the filename, so we create a new method to load the file. Once loaded, we delete the record and remove the file from the server.

That’s it, tutorial complete! If you run it, you should be able to upload a file, see it appear, and then delete it; all without leaving the page.


Final Thoughts

Obviously, the views can do with some prettying up, but this tutorial should have given you enough to be able to integrate this into your site.

There are a few shortcomings to this method, however:

  • You can only upload one file at a time, but this can rectified easily by using a service like Uploadify.
  • There is no progress bar built into the script.
  • We could reduce the SQL calls by updating the files div upon file upload, instead of fully replacing them.

Thanks for reading!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • Tolegen

    When I’ve entered http://localhost/CodeIgniter/index.php/Upload/index , browser showed me 404 Page Not Found. How can I fix this problem?

  • Stefano

    I think this guy stole your content… didn’t he? http://vapvarun.com/blog/2011/09/16/how-to-upload-files-with-codeigniter-and-ajax-wbcom/

    I just hate when that happens.

    thanks for the tutorial by the way!

  • ludo31

    great jobs !! thanks a lot !!

  • tyty

    have you the sources to donwload this ?

    thx for all

  • Tai

    I did exactly what you wrote on your post. Can you send me a demo version for this post via my email (mrdhtai@yahoo.com) ? Thanks

  • http://www.welancers.com Aslam Doctor

    This works only if you have jquery 1.2 version. Any higher version will give the error on console as
    ” Uncaught TypeError: Object function (a,b){return new e.fn.init(a,b,h)} has no method ‘handleError’ ”

    As the method “handleError” is not available in any higher version of jquery then 1.2 :(
    Any solution for this ?

    • Max

      had the same problem today… seems we can’t use this method anymore

  • http://webforbd.blogspot.com Rasel khan

    Thanks ! your post ………….

  • http://rochano Rochano

    AWESOME
    COOL MAN
    THKZ

  • http://www.vueine.com vueine

    You really powerful.
    Help my learning.

  • john

    Or you could just use the Ajax Framwork for CI ( http://codeigniter.com/wiki/Ajax_Framework_For_CodeIgniter )

    and use the $ajax->upload() API

  • A

    Good tut and it works! However….. If you turn on CRSF you get an this operation is not allowed error. Normally with ajax requests to get around this you set the cookie in js/jquery with something along the lines of the following……

    var crsf = $.cookies.get(‘cookie_name’);
    data['cookie_name'] = crsf;

    Also CodeIgniter has a built in function called ‘is_ajax_request()’. This is used so that you can return a 404 if someone/something manages to hit one of your ajax controllers. This is also fails as the incorrect headers are sent……

    I had a look in the ajaxfileupload.js file and couldn’t even find where the Ajax request is actually made. Does this even use ajax? Or is it just given that for namesake?

    If anyone knows better than me…. which they most likely do I would be keen to hear your thoughts……

  • A

    Solved. I hacked the ajaxfileupload.js so it carried the CSFR cookie and after digging around more I found out that you can’t actually upload a file with ajax but have to use a combination of iframes and forms which means it will never be an ajax request anyway. Therefore it can’t go in a controller that only accepts ajax reqs….

    • Vix

      Have you or anyone else managed to use this with newer version of jquery, like 1.7.2 ?

  • Alexander

    Awsome awsome awsome

  • hong gu

    hi
    thanks for your teaching
    i’m very enjoyful this homepage look

    uploading files is insert db ok
    but i have a quesetion
    how can i download this files?

    can you teaching me??

    thanks!

  • http://e-irfan.com Irfan

    always pleasure to visit nettuts

  • Colm

    Thanks for this, unfortunately im new to CI and it seems my AJAX is not responding/working. Whilst everything uploads to my db once i hit submit i need to refresh the page in order to update the files list. ?

  • Nicole

    I can’t get the tutorial to work. It doesn’t upload the file.

  • Sebastian

    Si funciona aquí adjunto el archivo para que lo descarguen funcionando

    seria asi localhost/upload/index.php/upload

    ya que tengo varias formas de cargar archivos

    el enlace de descarga es el siguiente

    http://www.4shared.com/rar/p80S9is0/upload.html?refurl=d1url

  • http://www.muabongda.com matth

    Thanks ! your post ………….

  • aldo

    Thanks for this great tutorial. may I know why the uploaded file name become random string like a0a0bcdc2f171935b3a6d0de5b3e6ae4.jpg? is there any way to make the uploaded file name just like the original?

    Thanks

  • http://www.facebook.com/anupam.rekha Anupam Rekha

    I get an error

    Error: TypeError: jQuery.handleError is not a function
    Source File: http://localhost/satyam/LifeBook/js/ajaxfileupload.js
    Line: 116

    • http://nookeen.com Arkadi (nookeen)

      handleError was deprecated starting JQuery 1.5, but what I did is simply replaced them with alert(‘my custom message’ ). There are just few of them. Or you can read more about it here:

      http://stackoverflow.com/questions/7866199/how-can-i-change-handleerror

    • Nasrullah Shamim

      finally found the problem. The problem is with AjaxFileUpload plugin of
      Jquery which I’m using. Instead of ‘json’ in dataType it requires it to
      be capitalized. dataType: ‘JSON’. Also after fixing this it
      automatically adds and to the beginning and end
      of the received json data. So it is not interpreted ad json.

  • http://www.facebook.com/brianbabu Brian Babu

    Can i get the source file for this? Thanks!

  • Parth Vikani`

    I need a source file of this tuorial , How can i download this files ?

  • dangui ismael kone

    very usefull tutorial. i had some problem loading the pix that i have uploaded to be able to delete it. i change your code a bit and it finally works hope i will help. i have just change the refresh file to this

    function refresh_files()
    {
    $.ajax({
    type: “GET”,
    url: “./upload/files/”,//the script to call to get data
    data:”,//you can insert url argumnets here to pass to api.php
    dataType: ‘html’,//data format
    success: function(data){//on recieve of reply
    $(‘#files’).html(data);
    }

    });
    }

  • souhila

    pleaaaaase how to download files