simpleUpload.js
Mục lục bài viết
simpleUpload.js
A jQuery file upload plugin with a hands-off approach to design.
Download v.1.1
$('#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
- 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)
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 – 13KB unminified – 45.7KB
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 ($success) {
$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 ($success) {
$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.
Notes
- 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!
- 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. - 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.
- 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.
- F*ck Flash
Example #1: Single File Upload with Progress Bar
is the URL to which the selected files should be uploaded. This will probably be a backend script of some kind.is an object containing all your options and callback functions. Here is a list of all the accepted options and callbacks.
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 filesize
: 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 uploadsuccess(data)
: finish the upload as successful and call thesuccess
callback, passing thedata
you provide to thesuccess
callbackerror(message)
: finish the upload as failed and call theerror
callback, passing themessage
you provide to theerror
callback with error typeHashError
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.
beforeSend
function(jqXHR, settings) {}
Context: File
Returned as Params
jqXHR
: (obj) The jqXHR object to modify before sending the AJAX request.
settings
: (obj) The settings object used in the AJAX request.
Description
New in v.1.1
. Called right before each AJAX request is made. Virtually identical to jQuery’s beforeSend. Use this function to modify the headers of a request, like so:
jqXHR.setRequestHeader('x-access-token', token);
Useful when you may want to attach a CSRF token. This function will not be called on older browsers that don’t support AJAX file upload.
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.
New in v.1.1
: If you have absolutely no control over the backend or still prefer to use HTTP error codes to indicate application error, you may use the xhr
property in the error
callback to capture error messages from your server. However, this will not be supported in older browsers.
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 typesmessage
: the error message for the specific error that occurredxhr
: thejqXHR
object that contains the response returned by the server
(only exists if AJAX request was made)
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.
New in v.1.1
: The xhr
property is useful when the server returns with a non-200 HTTP error code on application error and you need to capture the server’s error message. This isn’t reliable in older browsers and the xhr property won’t exist if there is a different type of error, but if an AJAX request was made and returns with a non-200 HTTP response code, you can expect this property. You should, in general, check for this property’s existence before using it.
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 thestart
callback.cancel()
: the callback function you can call to cancel the current upload. Will returntrue
if cancel was successful orfalse
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.
"Upload failed"
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.