simpleUpload.js
Unlike many JavaScript upload libraries on the interwebs, simpleUpload is an extremely simple yet powerful jQuery file upload plugin designed to be non-intrusive, backwards-compatible, flexible, and very easy to understand.
Features
- Multiple uploads
- File type and size filtering
- HTML5 Ajax upload with fallback to iframe
- Simultaneous uploads
- Plays nicely with drag-n-drop
- Cross-domain uploads
- Cancellable uploads
- Client-side hashing for integration with deduplication systems (premium feature only)
Browser Support
Desktop:
- Google Chrome
- Firefox 3+
- IE 6+
- Safari 4+
- Opera 10.6+
Mobile:
- iOS 6+
- Android 2.2+
- Windows Phone 8.1+
- Opera Mobile 11.50+
Note: IE8+ and Firefox 3.6+ required for cross-domain uploads
Basic Usage
$('#file').simpleUpload("/ajax/upload.php", { start: function(file){ //upload started }, progress: function(progress){ //received progress }, success: function(data){ //upload successful }, error: function(error){ //upload failed } });
Example #1: Single File Upload with Progress Bar
JavaScript
$(document).ready(function(){ $('input[type=file]').change(function(){ $(this).simpleUpload("/ajax/upload.php", { start: function(file){ //upload started $('#filename').html(file.name); $('#progress').html(""); $('#progressBar').width(0); }, progress: function(progress){ //received progress $('#progress').html("Progress: " + Math.round(progress) + "%"); $('#progressBar').width(progress + "%"); }, success: function(data){ //upload successful $('#progress').html("Success!<br>Data: " + JSON.stringify(data)); }, error: function(error){ //upload failed $('#progress').html("Failure!<br>" + error.name + ": " + error.message); } }); }); });
HTML
<div id="filename"></div><div id="progress"></div><div id="progressBar"></div> <input type="file" name="file">
Example #2: Multiple File Upload
JavaScript
$(document).ready(function(){ $('input[type=file]').change(function(){ $(this).simpleUpload("/ajax/upload.php", { /* * Each of these callbacks are executed for each file. * To add callbacks that are executed only once, see init() and finish(). * * "this" is an object that can carry data between callbacks for each file. * Data related to the upload is stored in this.upload. */ start: function(file){ //upload started this.block = $('<div class="block"></div>'); this.progressBar = $('<div class="progressBar"></div>'); this.block.append(this.progressBar); $('#uploads').append(this.block); }, progress: function(progress){ //received progress this.progressBar.width(progress + "%"); }, success: function(data){ //upload successful this.progressBar.remove(); /* * Just because the success callback is called doesn't mean your * application logic was successful, so check application success. * * Data as returned by the server on... * success: {"success":true,"format":"..."} * error: {"success":false,"error":{"code":1,"message":"..."}} */ if (data.success) { //now fill the block with the format of the uploaded file var format = data.format; var formatDiv = $('<div class="format"></div>').text(format); this.block.append(formatDiv); } else { //our application returned an error var error = data.error.message; var errorDiv = $('<div class="error"></div>').text(error); this.block.append(errorDiv); } }, error: function(error){ //upload failed this.progressBar.remove(); var error = error.message; var errorDiv = $('<div class="error"></div>').text(error); this.block.append(errorDiv); } }); }); });
HTML
<div id="uploads"></div> <input type="file" name="file" multiple>
Example #3: Cancellable Uploads (with file type & size filtering)
JavaScript
$(document).ready(function(){ $('input[type=file]').change(function(){ $(this).simpleUpload("/ajax/upload.php", { allowedExts: ["jpg", "jpeg", "jpe", "jif", "jfif", "jfi", "png", "gif"], allowedTypes: ["image/pjpeg", "image/jpeg", "image/png", "image/x-png", "image/gif", "image/x-gif"], maxFileSize: 5000000, //5MB in bytes start: function(file){ //upload started this.block = $('<div class="block"></div>'); this.progressBar = $('<div class="progressBar"></div>'); this.cancelButton = $('<div class="cancelButton">x</div>'); /* * Since "this" differs depending on the function in which it is called, * we need to assign "this" to a local variable to be able to access * this.upload.cancel() inside another function call. */ var that = this; this.cancelButton.click(function(){ that.upload.cancel(); //now, the cancel callback will be called }); this.block.append(this.progressBar).append(this.cancelButton); $('#uploads').append(this.block); }, progress: function(progress){ //received progress this.progressBar.width(progress + "%"); }, success: function(data){ //upload successful this.progressBar.remove(); this.cancelButton.remove(); if (data.success) { //now fill the block with the format of the uploaded file var format = data.format; var formatDiv = $('<div class="format"></div>').text(format); this.block.append(formatDiv); } else { //our application returned an error var error = data.error.message; var errorDiv = $('<div class="error"></div>').text(error); this.block.append(errorDiv); } }, error: function(error){ //upload failed this.progressBar.remove(); this.cancelButton.remove(); var error = error.message; var errorDiv = $('<div class="error"></div>').text(error); this.block.append(errorDiv); }, cancel: function(){ //upload cancelled this.block.fadeOut(400, function(){ $(this).remove(); }); } }); }); });
HTML
<div id="uploads"></div> <input type="file" name="file" multiple>
Server-side
No external scripts required. I have written these examples in PHP, but they can be written in any language.
Same-domain Upload
<?php /* * All of your application logic with $_FILES["file"] goes here. * It is important that nothing is outputted yet. */ // $output will be converted into JSON if ($sucess) { $output = array("success" => true, "message" => "Success!");} else { $output = array("success" => false, "error" => "Failure!");} if (($iframeId = (int)$_GET["_iframeUpload"]) > 0) { //old browser... header("Content-Type: text/html; charset=utf-8"); ?> <!DOCTYPE html><html><head></head><body><script type="text/javascript"> var data = { id: <?php echo $iframeId; ?>, type: "json", data: <?php echo json_encode($output); ?>}; parent.simpleUpload.iframeCallback(data); </script> </body></html><?php } else { //new browser... header("Content-Type: application/json; charset=utf-8"); echo json_encode($output); } ?>
Cross-domain Upload
<?php $remoteOrigin = "http://www.remote-domain.com"; //change to the origin of your webpage /* FOR AJAX CORS REQUESTS */ if ($_SERVER["HTTP_ORIGIN"]===$remoteOrigin) { header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]); /* Uncomment to allow cookies across domains */ //header("Access-Control-Allow-Credentials: true"); /* Uncomment to improve performance after testing */ //header("Access-Control-Max-Age: 86400"); // cache for 1 day } if ($_SERVER["REQUEST_METHOD"]==="OPTIONS") { if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_METHOD"])) header("Access-Control-Allow-Methods: GET, POST, OPTIONS"); if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"])) header("Access-Control-Allow-Headers: " . $_SERVER["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"]); exit(0); } /* END AJAX CORS CONFIGURATION */ /* * All of your application logic with $_FILES["file"] goes here. * It is important that nothing is outputted yet. */ // $output will be converted into JSON if ($sucess) { $output = array("success" => true, "message" => "Success!");} else { $output = array("success" => false, "error" => "Failure!");} if (($iframeId = (int)$_GET["_iframeUpload"]) > 0) { //old browser... header("Content-Type: text/html; charset=utf-8"); ?> <!DOCTYPE html><html><head></head><body><script type="text/javascript"> var data = { namespace: "simpleUpload", id: <?php echo $iframeId; ?>, type: "json", data: <?php echo json_encode($output); ?>, xDomain: "<?php echo $remoteOrigin; ?>"}; try { parent.simpleUpload.iframeCallback(data);} catch(e) { parent.postMessage(JSON.stringify(data), data.xDomain);} </script> </body></html><?php } else { //new browser... header("Content-Type: application/json; charset=utf-8"); echo json_encode($output); } ?>
I created simpleUpload.js because I could not find a solution for uploading files that was simple, efficient, non-intrusive, and backwards-compatible. Now it exists.