jQuery File Upload Example and Demo
When you develop an application, most, if not 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.
In this example, we will see how we can implement this using jQuery.
1. Introduction
So imagine you have to develop a file sharing tool and you want to support user registration and a functionality where users can upload a profile picture for their accounts. For simplicity, we are going to focus only on those two aspects that matter for this example: uploading a profile picture and uploading any kind of file for sharing.
2. Tools we are going to use
We will be using the following tools:
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:
composer init composer require slim/slim
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.
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
Your composer.json file should look like:
compose.json
{ "name": "webcodegeeks/jquery-file-upload", "description": "A jQuery file upload example", "license": "MIT", "authors": [ { "name": "Eivar Montenegro" } ], "require": { "slim/slim": "~2.6" } }
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 require 'vendor/autoload.php'; $app = new \Slim\Slim(); $app->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();
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.
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
<html> <head></head> <body> <?php if (!empty($app->errors)): ?> <div id="errors"> <?php foreach ($app->errors as $key => $error) : echo "$key: " . var_export($error, true); endforeach ?> </div> <?php endif ?> <?php if (!empty($app->message)): ?> <div id="notices"> <?php echo "File {$app->message['name']} uploaded successfully!"; ?> </div> <?php endif ?> <form method="POST" action="/profile/<?php echo $userId ?>" enctype="multipart/form-data" > <input type="file" name="profile_pic" value=""/> <input type="submit" value="Upload profile picture"/> </form> </body> </html>
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:
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
And we can update our index.php to use new file upload library.
index.php
<?php require 'vendor/autoload.php'; $appPath = dirname(__FILE__) ; $app = new \Slim\Slim(); $app->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();
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.
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
<html> <head></head> <body> <?php if (!empty($app->errors)): ?> <div id="errors"> <?php foreach ($app->errors as $key => $error) : echo "$key: " . var_export($error, true); endforeach ?> </div> <?php endif ?> <?php if (!empty($app->messages)): ?> <div id="notices"> <?php foreach ($app->messages as $message) : echo "<p>File {$message['name']} uploaded successfully!<br/></p>"; endforeach ?> </div> <?php endif ?> <form method="POST" action="/file-upload" enctype="multipart/form-data" > <input type="file" name="a_file" value=""/> <input type="submit" value="Upload file"/> </form> </body> </html>
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 require 'vendor/autoload.php'; $appPath = dirname(__FILE__) ; $app = new \Slim\Slim(); $app->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();
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
.
4. Front end (jQuery + Dropper)
It is time to improve our front end using jQuery. I am using bower to download all required javascript libraries.
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:
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
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:
{ "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" } }
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
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>jQuery file upload example</title> <link rel="stylesheet" type="text/css" href="/bower_components/bootstrap/dist/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="/bower_components/Dropper/jquery.fs.dropper.min.css"/> </head> <body> <div class="container"> <div class="row"> <div id="freedback" class="col-md-12"> </div> </div> <form method="POST" action="/profile/<?php echo $userId ?>" enctype="multipart/form-data" > <div class="form-group"> <div id="dropzone" class="dropper dropzone"> </div> </div> </form> </div> <script type="text/javascript" src="/bower_components/jquery/dist/jquery.min.js"></script> <script type="text/javascript" src="/bower_components/Dropper/jquery.fs.dropper.min.js"></script> <script type="text/javascript" src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> <script type="text/javascript"> (function ($) { $(function() { $('#dropzone').dropper({ action: '/profile/<?php echo $userId ?>', postKey: 'profile_pic', maxSize: 5242880, //5M maxQueue: 10 }) .on('start.dropper', function (e, files) { $('#freedback').empty().append('<div class="alert alert-info">Upload stared</div>'); }) .on('fileStart.dropper', function (e, file) { $('#freedback').append('<div class="alert alert-info">Upload stared [' + file.name + '] ' + '0%</div>'); }) .on('fileProgress.dropper', function (e, file, percent) { $('#freedback').append('<div class="alert alert-info">Upload progress [' + file.name + '] ' + percent +'%</div>'); }) .on('fileComplete.dropper', function (e, file, response) { $('#freedback').append('<div class="alert alert-info">Upload completed [' + file.name + '] <br/>' + 'server response: ' + response +'</div>'); if (response.errors) { $.each(response.errors, function (index, error) { $('#freedback').append('<div class="alert alert-danger">Server error: [' + error+ '] <br/></div>'); }); } if (response.message) { //$.each(response.message, function (index, message) { $('#freedback').append('<div class="alert alert-success">Upload success: [' + response.message.name + '] <br/></div>'); //}); } }) .on('fileError.dropper', function (e, file, error) { $('#freedback').append('<div class="alert alert-danger">' + file.name + ': ' + error + '</div>'); }) .on('complete.dropper', function (e) { $('#freedback').append('<div class="alert alert-success">All done!</div>'); }); }); })(jQuery); </script> </body> </html>
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.
4.2 File share
Now we gotta repeat the process with file_share_form.php
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>jQuery file upload example</title> <link rel="stylesheet" type="text/css" href="/bower_components/bootstrap/dist/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="/bower_components/Dropper/jquery.fs.dropper.min.css"/> </head> <body> <div class="container"> <div class="row"> <div id="freedback" class="col-md-12"> </div> </div> <form method="POST" action="/file-upload" enctype="multipart/form-data" > <div class="form-group"> <div id="dropzone" class="dropper dropzone"> </div> </div> </form> </div> <script type="text/javascript" src="/bower_components/jquery/dist/jquery.min.js"></script> <script type="text/javascript" src="/bower_components/Dropper/jquery.fs.dropper.min.js"></script> <script type="text/javascript" src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> <script type="text/javascript"> (function ($) { $(function() { $('#dropzone').dropper({ action: '/file-upload', postKey: 'file', maxSize: 5242880, //5M maxQueue: 10 }) .on('start.dropper', function (e, files) { $('#freedback').empty().append('<div class="alert alert-info">Upload stared</div>'); }) .on('fileStart.dropper', function (e, file) { $('#freedback').append('<div class="alert alert-info">Upload stared [' + file.name + '] ' + '0%</div>'); }) .on('fileProgress.dropper', function (e, file, percent) { $('#freedback').append('<div class="alert alert-info">Upload progress [' + file.name + '] ' + percent +'%</div>'); }) .on('fileComplete.dropper', function (e, file, response) { $('#freedback').append('<div class="alert alert-info">Upload completed [' + file.name + '] <br/>' + 'server response: ' + response +'</div>'); if (response.errors) { $.each(response.errors, function (index, error) { $('#freedback').append('<div class="alert alert-danger">Server error: [' + error+ '] <br/></div>'); }); } if (response.message) { $.each(response.message, function (index, message) { $('#freedback').append('<div class="alert alert-success">Upload success: [' + message.name + '] <br/></div>'); }); } }) .on('fileError.dropper', function (e, file, error) { $('#freedback').append('<div class="alert alert-danger">' + file.name + ': ' + error + '</div>'); }) .on('complete.dropper', function (e) { $('#freedback').append('<div class="alert alert-success">All done!</div>'); }); }); })(jQuery); </script> </body> </html>
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 require 'vendor/autoload.php'; $appPath = dirname(__FILE__) ; $app = new \Slim\Slim(); $app->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();
5. Download
You can download the full source code of this example here: jQuery File Upload Example
Hi,
Thanks for this tutorial, I downloaded the source code but it looks like it is missing the folder : vendor.
Thanks.
Hello
the vendor folder is created by composer, that folder contains third party dependecies so you need to use something like “composer install” to create it, take a look here: https://getcomposer.org/doc/00-intro.md