$('#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
	}

});

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.

Free to use under the MIT License.

Features

Works with all major browsers

It even works with IE6. I know. I tested them all.

Chrome
Firefox 3+
IE 6+
Safari 4+
Opera 10.6+
Mobile Support: iOS 6+, Android 2.2+, Windows Phone 8.1+, Opera Mobile 11.50+
(Note: IE8+ and Firefox 3.6+ required for cross-domain uploads)

How to Install

1. Download

Download simpleUpload.js, minified or unminified, and copy it to your server.

minified – 12.8KB   unminified – 44.9KB

Or, NPM it...

simpleUpload.js is also available via the NPM JavaScript package manager here. Eat your heart out.



npm i jquery-simple-upload

2. Client-side Boilerplate

You can either follow the more intricate examples in Example Code, or you can rip off this simple boilerplate. Make sure to change "/js/simpleUpload.min.js" to the path of simpleUpload on your server, and "/ajax/upload.php" to the path of your backend script. Note that simpleUpload.js requires jQuery >= 1.7.0.



<!DOCTYPE html>
<html>
<head>

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="/js/simpleUpload.min.js"></script>

<script type="text/javascript">

$(document).ready(function(){

	$('input[type=file]').change(function(){

		$(this).simpleUpload("/ajax/upload.php", {

			start: function(file){
				//upload started
				console.log("upload started");
			},

			progress: function(progress){
				//received progress
				console.log("upload progress: " + Math.round(progress) + "%");
			},

			success: function(data){
				//upload successful
				console.log("upload successful!");
				console.log(data);
			},

			error: function(error){
				//upload failed
				console.log("upload error: " + error.name + ": " + error.message);
			}

		});

	});

});

</script>

</head>
<body>

<input type="file" name="file">

</body>
</html>

Open up your JavaScript console to view the output (CTRL+SHIFT+J for Chrome; CMD+OPT+J on Mac)

3. Server-side Boilerplate

In order to keep simpleUpload backwards-compatible, only a few extra steps need to be taken. Depending on whether you are uploading on the same domain name or a different domain, things will differ slightly. These examples are written in PHP, but because no outside scripts or libraries are required, this can be easily converted into any language.

Same-domain Uploads

How to upload on the same domain and return data as JSON.



<?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 Uploads

How to upload on a different domain and return data as JSON.



<?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);

}

?>

Remember to change $remoteOrigin at the top to refer to the origin of your frontend webpage. This must include the protocol, host, and any non-standard port number without a trailing slash.

Returning data in other formats

To return data in a format other than JSON, you can do so by specifying the format following type in the data object. Note that data of any other type must also be enclosed in quotes. This is because JSON is the only type that seamlessly translates into a JavaScript object; everything else must be returned as a string. So, for example, instead of this:



var data = {
	id: <?php echo $iframeId; ?>,
	type: "json",
	data: <?php echo json_encode($JSON_output); ?>
};

You would write something like this (for XML):



var data = {
	id: <?php echo $iframeId; ?>,
	type: "xml",
	data: "<?php echo addslashes($XML_output); ?>"
};

When not using JSON, it's important to escape quotes so as not to screw up the JavaScript. Inside data, type can be any one of five values: "json", "xml", "html", "script", or "text". Choose the one that's appropriate for your data type. Unless you override this value with the expect option, your data will be outputted either as an object or string, depending on the type.

Additionally, the Content-Type header towards the bottom of your script (when doing an AJAX upload) should also match your type. Below is how your code should look for each different data type.

JSON



	header("Content-Type: application/json; charset=utf-8");
	echo json_encode($output);

XML



	header("Content-Type: text/xml; charset=utf-8");
	echo $output;

HTML



	header("Content-Type: text/html; charset=utf-8");
	echo $output;

Script



	header("Content-Type: text/javascript; charset=utf-8");
	echo $output;

Text



	header("Content-Type: text/plain; charset=utf-8");
	echo $output;

Example Code

First, the Basics...

Using jQuery, a typical simpleUpload call will look like this:



$(fileInput).simpleUpload(url, options);

fileInput is the file input element (<input type="file">) or set of elements from which to upload files.

So, in other words, this:  

url is the URL to which the selected files should be uploaded. This will probably be a backend script of some kind.
options is an object containing all your options and callback functions. Here is a list of all the accepted options and callbacks.

Notes

  1. Calling simpleUpload will submit your uploads immediately, unlike some other file upload libraries. If you want to delay execution until a later point, like when a user presses "submit", just call simpleUpload when you're ready!
  2. Your file input does not need a surrounding form. When you call simpleUpload, the file input element alone is basically cloned and submitted through its own hidden form, and the value of the original one is reset. Of course, you can send other data along with your upload using the data option.
  3. Files are always uploaded individually, one-per-request, regardless of whether it is an AJAX or iframe upload. When you start dealing with multiple files in multiple requests, things get complicated fast. This way, not only do you avoid hitting upload limits on your server, but also your code is much cleaner and predictable. Hence, simple.
  4. simpleUpload takes care of all the backwards-compatibility, so you don't have to. Browsers that support HTML5 will upload files using AJAX, whereas older browsers will silently fall back to using iframes. You don't have to take any extra steps to support old browsers client-side, and only minimal changes need to be made server-side.
  5. F*ck Flash

Example #1: Single File Upload with Progress Bar

Usually, you will probably want to upload a file right after it has been selected. To do so, you simply hook up simpleUpload to the onchange event for the file input. You probably also want a progress bar, so this example shows how to do that as well.

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

CSS



#progressBar {
	background-color: #3E6FAD;
	width: 0px;
	height: 30px;
	margin-top: 10px;
	margin-bottom: 10px;
	-moz-border-radius: 5px;
	-webkit-border-radius: 5px;
	-o-border-radius: 5px;
	border-radius: 5px;
	-moz-transition: .25s ease-out;
	-webkit-transition: .25s ease-out;
	-o-transition: .25s ease-out;
	transition: .25s ease-out;
}

View in Action

(For the sake of the example, CSS styling has been applied and uploads are rejected after 30 seconds by the server)

Example #2: Multiple File Upload

Multiple files can be selected at once in HTML5-capable browsers. This is useful if you would like to allow users to upload a collection of files versus only 1 file. Using the limit option, you can also restrict how many files can be uploaded at once.

JavaScript



$(document).ready(function(){

	$('input[type=file]').change(function(){

		$(this).simpleUpload("/ajax/upload.php?getFormat=1", {

			/*
			 * 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

CSS



#uploads .block {
	display: inline-block;
	vertical-align: top;
	width: 100px;
	height: 100px;
	margin-right: 20px;
	margin-bottom: 20px;
	padding: 10px;
	background-color: white;
	border: 1px solid #CCCCCC;
}

#uploads .block .progressBar {
	background-color: #3E6FAD;
	width: 0px;
	height: 5px;
	margin-top: 47px;
	-moz-border-radius: 5px;
	-webkit-border-radius: 5px;
	-o-border-radius: 5px;
	border-radius: 5px;
	-moz-transition: .25s ease-out;
	-webkit-transition: .25s ease-out;
	-o-transition: .25s ease-out;
	transition: .25s ease-out;
}

#uploads .block .format {
	text-align: center;
	font-size: 20px;
	font-weight: bold;
	margin-top: 34px;
}

#uploads .block .error {
	text-align: left;
	font-size: 14px;
	color: red;
}

View in Action

(For the sake of the example, CSS styling has been applied and uploads are rejected after 30 seconds by the server)

Note: While you can select an infinite number of files at once, the maximum number of simultaneous uploads is hard-coded at 10. However, individual browsers also limit the number of simultaneous HTTP connections, often from 2-8. If this limit has been exceeded, the "start" callback is still executed immediately for every file but the actual uploads are silently queued until they can be handled. You can see specific browser limits here.

Example #3: Cancellable Uploads

In this example, we will allow the user to upload multiple files and give them an interface with which they can cancel individual uploads. To cancel an upload inside a callback, it's just a matter of calling this.upload.cancel(). While we're at it, we will also restrict uploads to either JPEG, GIF, or PNG images with a maximum file size of 5MB.

JavaScript



$(document).ready(function(){

	$('input[type=file]').change(function(){

		$(this).simpleUpload("/ajax/upload.php?getFormat=1", {

			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

CSS



#uploads .block {
	position: relative;
	display: inline-block;
	vertical-align: top;
	width: 100px;
	height: 100px;
	margin-right: 20px;
	margin-bottom: 20px;
	padding: 10px;
	background-color: white;
	border: 1px solid #CCCCCC;
}

#uploads .block .progressBar {
	background-color: #3E6FAD;
	width: 0px;
	height: 5px;
	margin-top: 47px;
	-moz-border-radius: 5px;
	-webkit-border-radius: 5px;
	-o-border-radius: 5px;
	border-radius: 5px;
	-moz-transition: .25s ease-out;
	-webkit-transition: .25s ease-out;
	-o-transition: .25s ease-out;
	transition: .25s ease-out;
}

#uploads .block .cancelButton {
	position: absolute;
	top: -10px;
	right: -10px;
	width: 25px;
	height: 25px;
	background-color: black;
	border: 2px solid white;
	color: white;
	text-align: center;
	font-weight: bold;
	-moz-border-radius: 25px;
	-webkit-border-radius: 25px;
	-o-border-radius: 25px;
	border-radius: 25px;
	-moz-box-shadow: 0px 0px 10px 0px #C4C4C4;
	-webkit-box-shadow: 0px 0px 10px 0px #C4C4C4;
	-o-box-shadow: 0px 0px 10px 0px #C4C4C4;
	box-shadow: 0px 0px 10px 0px #C4C4C4;
	cursor: pointer;
}

#uploads .block .format {
	text-align: center;
	font-size: 20px;
	font-weight: bold;
	margin-top: 34px;
}

#uploads .block .error {
	text-align: left;
	font-size: 14px;
	color: red;
}

View in Action

(For the sake of the example, CSS styling has been applied and uploads are rejected after 30 seconds by the server)

Documentation

Hello, simpleUpload

Assuming your typical simpleUpload call will look like this:



$(fileInput).simpleUpload(url, options);

fileInput

Either a DOM object or jQuery object referring to a file input element or set of file input elements from which you would like to upload files. For each file input element, a simpleUpload instance is created and all of its options and callbacks refer to that instance. So, given the following HTML:

running:



$('input[type=file]').simpleUpload(url, options);

is the equivalent of running:



$('#file1').simpleUpload(url, options);
$('#file2').simpleUpload(url, options);
$('#file3').simpleUpload(url, options);

Each of these is an upload instance, and all the files from each file selection will be uploaded.

url

The string URL of the endpoint to which you would like to upload your files. This will most likely be a backend script, for example, /ajax/upload.php. If uploading to a cloud service, like Amazon S3, this may be something like https://bucket-name.s3.amazonaws.com/. If you happen to be uploading to Amazon S3, also don't forget to set up CORS and use the data option to include your key, acl, AWSAccessKeyId, policy, and signature. I don't happen to have a tutorial on how to use simpleUpload with Amazon S3 specifically yet, but the second half of this tutorial might help.

options

An object containing key-value pairs that can be used to define settings and callbacks for the upload instance.

Settings

Some basic settings that can do everything from limiting file uploads, filtering files, setting additional POST data, overriding the file input name, etc. Unlike other libraries that have 118 settings, we have 11. All the ones you'll ever need.

Key Data Type Description
name string The name of each file sent to the server. If using PHP, this will define the key of the file in the $_FILES array (e.g. $_FILES["name"]). If name is omitted, the name of the HTML file input element is used. If the element does not have a name, "file" is used.
data object A set of key-value pairs containing the POST data you would like to send to the server along with each file. This is not automatically populated by any surrounding forms.
limit integer The maximum number of files to accept from a single file input. A value of 0 means no limit. Note that this value is only relevant if multiple-file selections are enabled on the file input using the "multiple" attribute.
expect string The type of data expected to be received from the server upon a successful upload. Possible values are "auto", "json", "xml", "html", "script", or "text". If omitted, "auto" is the default. If "auto", the type is determined by the Content-Type header of the server's result (e.g. "Content-Type: application/json") if using HTML5. If not using HTML5, it is determined by the type property of the object sent back to the iframeCallback. If type is determined to be "json", an object is returned in the success callback. If type is "xml", an XML object is returned. If "script", the script is executed and returned as a string. If "html" or "text", the data is returned as plain text.
allowedExts array An array of acceptable file extensions which a file may have. If a file does not have one of the file extensions listed in the array, the error callback is called with an error type of InvalidFileExtensionError.

Note: Using this setting does not guarantee files with an invalid file extension will not be received by your server. You should still do server-side filtering of all input received to ensure your application is secure.
allowedTypes array An array of acceptable MIME types which a file may have. If a file does not have one of the MIME types listed in the array, the error callback is called with an error type of InvalidFileTypeError. This setting is only used for browsers that support HTML5, since this information is not available for pre-HTML5 browsers.

Note: Using this setting does not guarantee files with an invalid MIME type will not be received by your server. You should still do server-side filtering of all input received to ensure your application is secure.
maxFileSize integer The maximum acceptable size of each file in bytes. If a file is larger than maxFileSize, the error callback is called with an error type of MaxFileSizeError. This setting is only used for browsers that support HTML5, since this information is not available for pre-HTML5 browsers. A value of 0 means no limit.

Note: Using this setting does not guarantee files with an invalid size will not be received by your server. You should still do server-side filtering of all input received to ensure your application is secure.
files object simpleUpload does not handle drag-n-drop directly (I might work on that), but if you manage to grab a files object through another method, such as drag-n-drop, you can pass it directly to simpleUpload using this setting. In that case, you would call simpleUpload like this:

$.fn.simpleUpload("upload.php", { files: files });

Naturally, this only works with HTML5 browsers. Attempting this on an older browser will return an error of type UnsupportedError in the error callback:

"Your browser does not support this upload method"
xhrFields object A set of key-value pairs to set on the native XMLHttpRequest object during an Ajax upload. This could be used to set the withCredentials flag to true, for example, allowing cross-domain cookie access.

{ withCredentials: true }

Note that this flag will have no effect for older browsers not capable of Ajax file uploads, so I do not recommend relying on getting/setting cookies in your receiving script if it's a cross-domain upload. Instead, I recommend passing cookies over POST using the data setting.
forceIframe boolean If set to true, the upload will be performed using the fallback iframe method instead of Ajax. I do not recommend doing this on production systems. It is intended solely for debugging purposes. When first installing and using simpleUpload with your backend script, it is useful to know whether the iframe functionality is working correctly, for older browsers. Setting this to false will use Ajax by default, when available. This is the default value.
hashWorker string It has nothing to do with marijuana. It is the URL of the worker script that can be used to generate MD5, SHA-1, or SHA-256 hashes of each file. The generated hash is then sent to the hashComplete callback, where the upload process can be stopped or continued. It is recommended this worker is hosted on the same domain as the calling page, and the path given should be relative to the page.

If the worker is hosted on a different domain, it is still possible to reference it, but another javascript file must still be made on the same domain. Inside it, you can just import the remote worker:

importScripts("http://remote-domain.com/worker.js");

On your page, hashWorker would then refer to your same-domain script containing the above import.
This setting only applies if you have purchased a license to use client-side hashing. More information here.

Callbacks

Functions that you can set to execute upon certain events. The ones you are most likely to use will probably be start, progress, success, and error. Also, take note of each callback's context, which defines the value of this inside the callback.

Key Description
init function(total_uploads) {}
Context: Instance

Returned as Params

total_uploads: (int) The total number of files that are going to be uploaded.

Description

Called when an upload instance is initialized. It is only called once. Returning false in this callback will cancel all uploads for the instance. Returning an integer will define a new limit for the max number of files to upload. All uploads after the new limit will be considered cancelled. However, the cancel callback will not be called.
start function(file) {}
Context: File

Returned as Params

file: (obj) The file which is going to be uploaded. Included in file is:
  • name: filename of the file
  • size: the size of the file in bytes (only if HTML5)
  • type: the MIME type of the file or "" if it cannot be determined (only if HTML5)

Description

Called for each file at the start of each upload. Returning false in this callback will cancel the current upload. However, the cancel callback will not be called. The upload process is not actually started until this callback has completed.
hashComplete function(hash, callbacks) {}
Context: File

Returned as Params

hash: (string) The calculated hash resulting from the hashWorker.
callbacks: (obj) An object containing callback functions that can continue or stop the upload.

These callbacks include:
  • proceed(): continue the upload
  • success(data): finish the upload as successful and call the success callback, passing the data you provide to the success callback
  • error(message): finish the upload as failed and call the error callback, passing the message you provide to the error callback with error type HashError

Description

Called for each file after a hash has been calculated. The upload for this file is essentially halted until one of the callbacks has been called. When combining with a de-duplication system, this is useful because you can check whether a hash exists in your system without even uploading the file. By pausing the upload at this callback, you get the chance to check the file hash and either continue with the upload, if the hash doesn't exist, or finish it as successful, if the hash does exist. If the hash exists but the associated file is not valid for that upload process, you can finish it with an error instead. Each callback will return true if the callback executed successfully, and false if not, for example, if another callback was already called or the upload was cancelled elsewhere. Note that if any errors occur while calculating the hash, the default behavior is to proceed with the upload automatically.

Examples on how to use this callback can be found here.
This callback only applies if you have purchased a license to use client-side hashing. More information here.
progress function(progress) {}
Context: File

Returned as Params

progress: (float) The current progress of an upload, from 0-100.

Description

Called for each file when its upload progress has been updated. Note that this callback will not be called during an upload for pre-HTML5 browsers, since client-side upload progress can only be achieved in HTML5 browsers. When an upload is successful, however, this callback will be called with a value of 100 just before the success callback, for older browsers.
success function(data) {}
Context: File

Returned as Params

data: (varied) The data returned from the server, either as an object or string, as determined by the expect setting.

Description

Called for each file after it has uploaded successfully. For newer browsers, a successful upload is triggered only when the server returns with an HTTP status code of 200 and the outputted data can be parsed in accordance with the expect setting. In older browsers, the HTTP status code is ignored. A successful upload is only triggered by valid output that can be parsed. Given this, it is recommended that you pass application errors resulting from your backend script through your output instead of using HTTP status codes. In your success callback, you would simply check for those errors upon completion. This will ensure your application is completely backwards-compatible.
error function(error) {}
Context: File

Returned as Params

error: (obj) An object containing information related to an error. Properties include:
  • name: the type of error that occurred; See error types
  • message: the error message for the specific error that occurred

Description

Called for each file that has encountered an error. A list of possible errors and their types can be found here. For newer browsers, this callback will only be called if the server cannot be reached, an HTTP status code other than 200 was returned, or the output returned by the server could not be parsed in accordance with the expect setting. In older browsers, this callback will be called only if the server's output cannot be parsed. This is just because of the nature of how uploads are handled in older browsers.
cancel function() {}
Context: File

Description

Called for each file whose upload has been cancelled via the upload.cancel() function in this file's context. This callback is only called if the upload is currently active and has not yet reached success or error. Cancelling an upload inside the init or start callback also does not trigger this callback, unless it is cancelled after the upload has already started, such as through a "click" or "setTimeout" event. If this callback is called, you can be certain success and error will not be called for this file.
complete function(status) {}
Context: File

Returned as Params

status: (string) The status of the upload, either "success", "error", or "cancel".

Description

Called for each file after it has completed, regardless of success. This callback will be called immediately after the success, error, and cancel callbacks, regardless of which one was called. However, using the "status" parameter, you can determine the status of the upload. This callback is useful if you want to perform an action after an upload no matter how it ended.
finish function() {}
Context: Instance

Description

Called after all file uploads for this instance have completed. It is only called once. This callback may be useful if, for example, you want to re-enable the original file input after it was disabled in the init callback to prevent more files from being uploaded simultaneously.

Contexts

The context of a callback function is the value of this within the function. There are only two possible types of contexts within a function, instance and file.

Context Description
instance An object that can be used to store data related to the instance and retrieve the file contexts of all the files in the instance. By default, the context contains only one property, "files", a 0-indexed array of all the file contexts.

this = { files: [] }

To generate an array containing the state of each upload after all the uploads have finished, for example, you would run this:


$('input[type=file]').simpleUpload("/ajax/upload.php", {

	finish: function(){
		var states = [];
		for (var i in this.files) {
			states.push(this.files[i].upload.state);
		}
		console.log(states);
	}

});

This context is also shared with all callbacks of the same instance that use the same context. So, in other words, you can do the following:


$('input[type=file]').simpleUpload("/ajax/upload.php", {

	init: function(total_uploads){
		this.foo = "bar";
	},

	finish: function(){
		console.log(this.foo); //outputs "bar"
	}

});

Since this.files is an array of file contexts, see file below.
file An object that is specific to each file and can be used to store and retrieve data related to that file. By default, it only contains one property, "upload", an object containing data and callbacks related to the upload. Inside this.upload you have the properties:
  • index: the index of the file in the set of files, starting with 0.
  • state: the state of the upload. Possible states:
    • "init" (upload hasn't started yet)
    • "uploading" (after start callback is called)
    • "success" (before success callback is called)
    • "error" (before error callback is called)
    • "cancel" (before cancel callback is called)
  • file: the file object containing name, size (HTML5), and type (HTML5). This is the same file that is sent as a parameter to the start callback.
  • cancel(): the callback function you can call to cancel the current upload. Will return true if cancel was successful or false if upload already completed or was cancelled.
Like the instance context, you can share data about a file between callbacks that use the file context, which is most callbacks. This allows you to do things like this:


$('input[type=file]').simpleUpload("/ajax/upload.php", {

	start: function(file){
		this.foo = $('<div>').text("Uploading...");
		$('body').append(this.foo);
	},

	progress: function(progress){
		//cancel upload after it has uploaded over 50%
		if (progress > 50) {
			this.newColor = "red";
			this.upload.cancel();
		}
	},

	cancel: function(){
		this.foo.text(this.upload.file.name + " was cancelled")
				.css("color", this.newColor);
	}

});

Note: Calling cancel() on an upload does not guarantee the file will not be received by the server. It will simply attempt to stop the upload as soon as possible. After cancel() is called, however, the success and error callbacks will not be called.

Errors

These are all the possible errors and their types. By looking for these errors in your error callback, you can substitute these messages for some of your own. The type of error can be accessed using error.name and its message at error.message.

Name Description / Message
InvalidFileExtensionError When a file has a file extension other than one of the allowed extensions in the set passed to the allowedExts setting. This can only occur if allowedExts is set.

"That file format is not allowed"
InvalidFileTypeError When a file has a MIME type other than one of the allowed MIME types in the set passed to the allowedTypes setting. This can only occur if allowedTypes is set.

"That file format is not allowed"
MaxFileSizeError When a file has a file size larger than the maxFileSize setting. This can only occur if maxFileSize is set.

"That file is too big"
HashError When you have called the error callback inside the hashComplete callback with a custom error message.

User defined message
RequestError When either the server failed to be reached, an HTTP status code other than 200 was detected upon upload, or the output from the server could not be parsed in accordance with the expect setting.

"Could not get response from server"
UnsupportedError When the upload cannot complete because the user's browser does not support the upload method being used. Specifically, if the files setting is set to acquire files from a drag-n-drop operation, for example, this error only occurs if those files cannot be sent over Ajax, which would be required. This is a very rare case, only a possible occurrence in Safari versions 3.1, 3.2, and 4 used by no more than 0.13% of the population.

"Your browser does not support this upload method"
InternalError When an internal error has occurred. This basically won't happen unless you screw with things like the files object after an upload has started. Leave it alone and you'll be fine.

"There was an error uploading the file"

Miscellaneous Tips & Tricks

Upload one file at a time

Not sure why you would want to do this. But if you must…it can be done with the following call. You can actually set the maximum number of simultaneous uploads to any number you want. But remember it is still subject to browser limits.



$.fn.simpleUpload.maxSimultaneousUploads(1);

Restrict to images and audio

Because simpleUpload was originally developed as part of a personal project of mine having to do with music, I already took the time to find all the possible file extensions and MIME types for several image and audio formats. These may be of use to someone.

Images: JPEG, GIF, and PNG only



$('input[type=file]').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"]
});

Audio: AIFF, WAV, FLAC, OGG, MP3, M4A, and AAC only



$('input[type=file]').simpleUpload("/ajax/upload.php", {
	allowedExts: ["mp3", "wav", "wave", "flac", "ogg", "oga", "aac", "mp4", "m4a", "aiff", "aif", "aifc"],
	allowedTypes: ["audio/mpeg", "audio/mp3", "audio/x-mpeg", "audio/x-mp3", "audio/mpeg3", "audio/x-mpeg3", "audio/x-mpeg-3", "audio/mpg", "audio/x-mpg", "audio/x-mpegaudio", "audio/mpa", "audio/mpa-robust", "audio/wav", "audio/wave", "audio/x-wav", "audio/x-wave", "audio/x-pn-wav", "audio/vnd.wave", "audio/flac", "audio/x-flac", "audio/ogg", "audio/x-ogg", "application/ogg", "application/x-ogg", "audio/aac", "audio/aacp", "audio/x-aac", "application/octet-stream", "audio/mp4", "audio/m4a", "audio/x-m4a", "audio/mp4a", "audio/mp4a-latm", "audio/mpga", "audio/mpeg4-generic", "audio/x-m4a-lossless", "video/quicktime", "audio/aiff", "audio/x-aiff", "audio/x-aifc", "sound/aiff", "audio/x-pn-aiff"]
});

Override file input UI

The default file input element is not the sexiest in the world. Sometimes, you might want to prompt a file selection by clicking on a custom element instead. You can do this using a label, linking to your hidden file input.

CSS



.box {
	display: inline-block;
	width: 70px;
	height: 70px;
	background-color: white;
	border: 4px dashed #B5B5B5;
	color: #B5B5B5;
	font-size: 50px;
	text-align: center;
	padding: 30px;
}

View in Action

(For the sake of the example, CSS styling has been applied)

File selected: none

Although this is supported on most current browsers, some really old browsers will not allow you to assign a label for a file input. If you care to provide full backwards-compatible support, the best solution is to provide a button the user can press in case your custom element isn't working. When the user presses the button, it will then unhide the original file input, which the user can use to upload their file. Or, you can show the original file input in conjunction with your custom UI.