Advertisement
JavaScript & AJAX

How to Upload Files with CodeIgniter and AJAX

by

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!

Related Posts
  • Code
    Web Development
    Laravel Unwrapped: Session, Auth and CacheLaravel wide retina preview
    Join me as we learn how to use Laravel's component-based system, Illuminate. Additionally, we'll see how to utilize service providers, Laravel's manager system, the Session, Auth, and Cache components, and the Store, Guard, and Repository libraries.Read More…
  • Code
    Theme Development
    Custom Controls in the Theme CustomizerTheme customizer custom control 400
    In the last article, we explored the advanced controls available in the Theme Customizer, and how to implement them. We’re going to look at how to create our own custom control, allowing you to choose which Category of Posts are displayed on the home page. To get started, download version 0.6.0 of our Theme Customizer Example.Read More…
  • Code
    PHP
    Building a Customer Management App Using AngularJS and LaravelLaravel 4 auth retina preview
    When creating a single-page app we should use some kind of framework to do some of the job for us, so we can focus on the actual functionality. AngularJS fits here perfectly, because features like dynamic dependency injection and bi-directional data binding are just great. Sometimes we also require some kind of server. If you've chosen PHP then Laravel may be your best option, as it's easy to work with and pretty powerful.Read More…
  • Code
    PHP
    Creating a Photo Tag Wall With Twilio Picture Messaging & PHPProcedural to oop php retina preview
    Twilio's recently announced Picture Messaging has vastly opened up what we can do with text messaging, now we can attach photos to our text messages and have them get used in different ways. In our case, we are going to build a Photo Tag Wall, which will contain photos linked to tags that will be displayed on a website.Read More…
  • Code
    PHP
    Authentication With Laravel 4Laravel 4 auth retina preview
    Authentication is required for virtually any type of web application. In this tutorial, I'd like to show you how you can go about creating a small authentication application using Laravel 4. We'll start from the very beginning by creating our Laravel app using composer, creating the database, loading in the Twitter Bootstrap, creating a main layout, registering users, logging in and out, and protecting routes using filters. We've got a lot of code to cover, so let's get started!Read More…
  • Code
    PHP
    Building a CodeIgniter Web Application From Scratch - Part 1Web app retina preview
    In this series we're going to build a web billboard application from scratch, we're going to use CodeIgniter to handle the back-end service and BackboneJS for the web client. In the first two parts of the series we'll create the back-end service and then the client application in the last two.Read More…