When you develop and application most, if no all, of the times you will have to provide some way for your users to upload files, for example to update their profile picture or to share a document, so let's see how we can implement this using jQuery.

Table Of Contents

1. Introduction
2. Tools we are going to use
3. Back end (PHP)
3.1 Profile picture
3.2 File share
4. Front end (jQuery + Dropper)
4.1 Profile picture
4.2 File share
5. Download
 

1. Introduction

So imagine you have to develop a file sharing tool, you want to support user registration and the users can upload a profile picture for their accounts. For simplicity we are going to focus only on the two aspects that matter for this example uploading an profile picture and uploading any kind of file for sharing.

2. Tools we are going to use

We will be using the following tools:
  1. PHP 5.5
  2. sublime text 3
  3. Slim framework
  4. composer
  5. node.js v0.10.33
  6. bower
  7. GNU/Linux distro (Ubuntu)
TOC

3. Back end (PHP)

Let's start with the back end of our application, I am using Slim a PHP micro framework, to get the job done easily but you can implement it just anyway you want. First we need to create a composer project and install Slim, so open a terminal or command console and type: [bash] composer init composer require slim/slim [/bash] Composer will ask you a few questions and then it will generate a composer.json file, with composer require slim/slim we are installing Slim as a dependency, you should get an output like the following on your terminal. [bash] Using version ~2.6 for slim/slim ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing slim/slim (2.6.2) Downloading: 100% Writing lock file Generating autoload files [/bash] Your composer.json file should looks like: compose.json [javascript] { "name": "webcodegeeks/jquery-file-upload", "description": "A jQuery file upload example", "license": "MIT", "authors": [ { "name": "Eivar Montenegro" } ], "require": { "slim/slim": "~2.6" } } [/javascript] We are ready to create our index.php file and start working in the back end. Create a new file, name it index.php and add the following code to it: index.php [php] get('/profile/:userId', function ($userId) { echo 'Profile picture form will be shown here'; }); $app->post('/profile/:userId', function ($userId) { echo 'Profile picture upload will be processed here!'; }); $app->get('/file-upload', function () { echo 'File upload form will be shown here'; }); $app->post('/file-upload', function () { echo 'File upload will be processed here!'; }); $app->run(); [/php] Our index file is starting to have some structure, the simplest way to test our app is using PHP Built-id web server, just run the following command in your terminal php -S localhost:8080 and visit http://localhost:8080/profile/1 in your web browser, there should be a message displayed for you. TOC

3.1 Profile picture

We can now start working on our profile picture upload; create a new file and name it profile_pic_form.php and add the following code to it: profile_pic_form.php [php] errors)): ?>
errors as $key => $error) : echo "$key: " . var_export($error, true); endforeach ?>
message)): ?>
message['name']} uploaded successfully!"; ?>
[/php] Here we have defined a simple html form, there are also two conditions to show errors or a message if such variables are not empty, that is just to provide some feedback to the user. At this point you should be able to select a file from your computer and upload it to our testing back end just remember to start the PHP built-in server using php -S ...; Your files would be stored in uploads folder. We are going to use codeguy/upload a third party library to handle file uploads, let's add it just like we did with Slim: [bash] composer require codeguy/upload Using version ~1.3 for codeguy/upload ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing codeguy/upload (1.3.2) Downloading: 100% Writing lock file Generating autoload files [/bash] And we can update our index.php to use new file upload library.
Tip
Make sure that your php.ini file is configured correctly, specially upload_max_filesize
index.php [php] storage = new \Upload\Storage\FileSystem($appPath . '/uploads'); $app->get('/profile/:userId', function ($userId) { include_once 'profile_pic_form.php'; }); $app->post('/profile/:userId', function ($userId) use($app) { $file = new \Upload\File('profile_pic', $app->storage); $newFilename = uniqid(); $file->setName($newFilename); // Validate file // MimeType List => http://www.webmaster-toolkit.com/mime-types.shtml $file->addValidations([ new \Upload\Validation\Mimetype(['image/png', 'image/jpeg']), new \Upload\Validation\Size('5M'), ]); try { $app->message = [ 'name' => $file->getNameWithExtension(), 'mime' => $file->getMimetype(), 'size' => $file->getSize(), 'md5' => $file->getMd5(), 'dimensions' => $file->getDimensions() ]; $file->upload(); } catch (\Exception $e) { $app->errors = $file->getErrors(); } include_once 'profile_pic_form.php'; }); $app->get('/file-upload', function () { echo 'File upload form will be shown here'; }); $app->post('/file-upload', function () { echo 'File upload will be processed here!'; }); $app->run(); [/php] We have implemented the basic process to upload a file, the most important part here is the post route /profile/:userId because there we create an \Upload\File instance, we add a MIME type validation and store the uploaded file.
Tip
For more details on codeguy/upload you can check the github.com project
TOC

3.2 File share

Now lets do the same for our file sharing routes, first the upload form. Create the file file_share_form.php, open it and add the following source code: file_share_form.php [php] errors)): ?>
errors as $key => $error) : echo "$key: " . var_export($error, true); endforeach ?>
messages)): ?>
messages as $message) : echo "

File {$message['name']} uploaded successfully!

"; endforeach ?>
[/php] Our file sharing form is very similar to the one we used for a profile picture, it's ok for now we only want to make sure that we can upload a document. Open your index.php file and add the file upload processing code for /file-upload route: index.php [php] storage = new \Upload\Storage\FileSystem($appPath . '/uploads'); $app->get('/profile/:userId', function ($userId) { include_once 'profile_pic_form.php'; }); $app->post('/profile/:userId', function ($userId) use($app) { $file = new \Upload\File('profile_pic', $app->storage); $newFilename = uniqid(); $file->setName($newFilename); // Validate file // MimeType List => http://www.webmaster-toolkit.com/mime-types.shtml $file->addValidations([ new \Upload\Validation\Mimetype(['image/png', 'image/jpeg']), new \Upload\Validation\Size('5M'), ]); try { $app->message = [ 'name' => $file->getNameWithExtension(), 'mime' => $file->getMimetype(), 'size' => $file->getSize(), 'md5' => $file->getMd5(), 'dimensions' => $file->getDimensions() ]; $file->upload(); } catch (\Exception $e) { $app->errors = $file->getErrors(); } include_once 'profile_pic_form.php'; }); $app->get('/file-upload', function () { include_once 'file_share_form.php'; }); $app->post('/file-upload', function () use($app) { $messages = []; foreach ($_FILES as $key => $data) { $file = new \Upload\File($key, $app->storage); $file->setName(uniqid()); // Validate file // MimeType List => http://www.webmaster-toolkit.com/mime-types.shtml $file->addValidations([ new \Upload\Validation\Mimetype(['image/png', 'image/jpeg', 'application/pdf']), new \Upload\Validation\Size('5M'), ]); try { array_push($message, [ 'name' => $file->getNameWithExtension(), 'mime' => $file->getMimetype(), 'size' => $file->getSize(), 'md5' => $file->getMd5(), 'dimensions' => $file->getDimensions() ]); $file->upload(); } catch (\Exception $e) { $app->errors = $file->getErrors(); break; } } $app->messages = $messages; include_once 'file_share_form.php'; }); $app->run(); [/php] Now when you post a file or files to /file-upload, each one of them will be saved and details of each file will be stored on $app->message. TOC

4. Front end (jQuery + Dropper)

Is time for improving our front end using jQuery, I am using bower to download all required javascript libraries.
Tip
Bower is a package manager for the web, is optimized for the front-end and keep track of downloaded packages in a manifest file named: bower.json
To install bower you must install node.js first, then use npm to install bower like this: npm install -g bower. Once bower is installed in your system you can use it like bower install <package> Now we will install Dropper, jquery and bootstrap. Dropper is a jQuery plugin for simple drag and drop uploads. You can install Dropper using the following command on your terminal: bower install Dropper, this should produce an output similar to: [bash] bower not-cached git://github.com/benplum/Dropper.git#* bower resolve git://github.com/benplum/Dropper.git#* bower download https://github.com/benplum/Dropper/archive/1.0.1.tar.gz bower extract Dropper#* archive.tar.gz bower resolved git://github.com/benplum/Dropper.git#1.0.1 bower install Dropper#1.0.1 Dropper#1.0.1 bower_components/Dropper [/bash] Also install jquery bower install jquery and bootstrap bower install bootstrap and make sure that your dependencies get saved to bower.json executing bower init answer the questions presented by bower and you will get a bower.json file similar to: [javascript] { "name": "jquery-file-upload", "version": "0.0.0", "authors": [ "Eivar Montenegro" ], "main": "index.php", "moduleType": [ "node" ], "license": "MIT", "private": true, "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests" ], "dependencies": { "Dropper": "~1.0.1", "jquery": "~2.1.3", "bootstrap": "~3.3.4" } } [/javascript] TOC

4.1 Profile picture

We have to include the resources installed by bower, you can find those files in bower_components folder, let's modify profile_pic_form.php: [php] jQuery file upload example
[/php] Few things have changed, we no longer reload the entire page, so only the results of the upload process are returned in json format, we have several functions that are called in response to Dropper events, the old form was replaced with a drop zone and finally I have added all the bootstrap boilerplate code. TOC

4.2 File share

Now we gotta repeat the process with file_share_form.php: [php] jQuery file upload example
[/php] Just like before we have some functions for handling all the events triggered by Dropper, we replaced the from with a drop zone and all the bootstrap boilerplate was added. Last but not least, we need to update index.php so it will send the correct format (JSON), here is the final index.php file: [php] storage = new \Upload\Storage\FileSystem($appPath . '/uploads'); $app->get('/profile/:userId', function ($userId) { include_once 'profile_pic_form.php'; }); $app->post('/profile/:userId', function ($userId) use($app) { $app->response->headers->set('Content-Type', 'application/json'); $file = new \Upload\File('profile_pic', $app->storage); $newFilename = uniqid(); $file->setName($newFilename); // Validate file // MimeType List => http://www.webmaster-toolkit.com/mime-types.shtml $file->addValidations([ new \Upload\Validation\Mimetype(['image/png', 'image/jpeg']), new \Upload\Validation\Size('5M'), ]); $response = ['message' => null, 'errors' => null]; try { $response['message'] = [ 'name' => $file->getNameWithExtension(), 'mime' => $file->getMimetype(), 'size' => $file->getSize(), 'md5' => $file->getMd5(), 'dimensions' => $file->getDimensions() ]; $file->upload(); $app->response->setStatus(201); } catch (\Exception $e) { $response['errors'] = $file->getErrors(); $response['message'] = null; $app->response->setStatus(200); } echo json_encode($response); }); $app->get('/file-upload', function () { include_once 'file_share_form.php'; }); $app->post('/file-upload', function () use($app) { $app->response->headers->set('Content-Type', 'application/json'); $response = ['message' => [], 'errors' => null]; foreach ($_FILES as $key => $data) { $file = new \Upload\File($key, $app->storage); $file->setName(uniqid()); // Validate file // MimeType List => http://www.webmaster-toolkit.com/mime-types.shtml $file->addValidations([ new \Upload\Validation\Mimetype(['image/png', 'image/jpeg', 'application/pdf']), new \Upload\Validation\Size('5M'), ]); try { array_push($response['message'], [ 'name' => $file->getNameWithExtension(), 'mime' => $file->getMimetype(), 'size' => $file->getSize(), 'md5' => $file->getMd5(), 'dimensions' => $file->getDimensions() ]); $file->upload(); $app->response->setStatus(201); } catch (\Exception $e) { $response['errors'] = $file->getErrors(); $app->response->setStatus(200); break; } } echo json_encode($response); }); $app->run(); [/php] TOC

5. Download

Download
You can download the full source code of this example here: jQuery File Upload Example
TOC