View on GitHub

Simple-ajax-uploader

Javascript plugin for cross-browser Ajax file uploading with progress bar support. Works in all major browsers, including IE7+, Chrome, Firefox, Safari, and Opera. No dependencies - use it with or without jQuery.

Download this project as a .zip file Download this project as a tar.gz file

Simple Ajax Uploader

A Javascript plugin for cross-browser Ajax file uploading. Supports multiple file uploading with progress bars.

Live demo: http://www.lpology.com/code/ajaxuploader/

Overview

Simple Ajax Uploader allows developers to easily add Ajax file upload functionality to web applications. It abstracts away standard tasks and browser compatibility issues while preserving wide latitude for custom use.

The project began as a rewrite of Andrew Valum's original Ajax Upload plugin. The goal of the project is make file uploading easy for developers and pleasant for users. Basic usage:

var uploader = new ss.SimpleUpload({
      button: 'upload-btn', // HTML element used as upload button
      url: '/PathTo/UploadHandler', // URL of server-side upload handler
      name: 'uploadfile' // Parameter name of the uploaded file
});

Features

How to Use

There are two main ways to use the plugin:

1. Single file uploading - Only one upload allowed at a time. Progress bar is an element that is re-used for each upload.
2. Multiple file uploading - Allow multiple, concurrent file uploads. Progress bars are created on the fly before each upload.

Method 1: Single file uploading (one file at a time)

Before each upload, in the onSubmit() callback function, the on-page sizeBox and progress elements are assigned specific roles using these two functions:

setProgressBar(elem) - Designates an element as the progress bar for an upload.
setFileSizeBox(elem) - Designates an element as the container in which the file size of an uploading file will be inserted.

As a result, when an upload begins, the file size of the upload is inserted into the sizeBox element and the CSS width of the progress element is set to 0%. As the upload progresses, the CSS width percentage of the progress element will be updated accordingly.

This approach of assigning roles to elements provides developers with a great deal of flexibility -- progress indicators can be styled in any way and placed anywhere on the page.

var sizeBox = document.getElementById('sizeBox'), // container for file size info
    progress = document.getElementById('progress'); // the element we're using for a progress bar

var uploader = new ss.SimpleUpload({
      button: 'uploadButton', // file upload button
      url: 'uploadHandler.php', // server side handler
      name: 'uploadfile', // upload parameter name        
      progressUrl: 'uploadProgress.php', // enables cross-browser progress support (more info below)
      responseType: 'json',
      allowedExtensions: ['jpg', 'jpeg', 'png', 'gif'],
      maxSize: 1024, // kilobytes
      hoverClass: 'ui-state-hover',
      focusClass: 'ui-state-focus',
      disabledClass: 'ui-state-disabled',
      onSubmit: function(filename, extension) {
          this.setFileSizeBox(sizeBox); // designate this element as file size container
          this.setProgressBar(progress); // designate as progress bar
        },         
      onComplete: function(filename, response) {
          if (!response) {
              alert(filename + 'upload failed');
              return false;            
          }
          // do something with response...
        }
});        

Method 2: Multiple file uploads

Below is an example of how to implement multiple file uploading with progress bars. A new progress bar is created for each file upload within the onSubmit() callback function.

Like in Method 1, the newly created elements are assigned roles using the setProgressBar() and setFileSizeBox() functions. Unlike the previous example, however, the progress elements are automatically removed when the upload is completed.

var uploader = new ss.SimpleUpload({
      button: 'uploadButton',
      url: 'uploadHandler.php', // server side handler
      progressUrl: 'uploadProgress.php', // enables cross-browser progress support (more info below)
      responseType: 'json',
      name: 'uploadfile',
      multiple: true,
      allowedExtensions: ['jpg', 'jpeg', 'png', 'gif'], // for example, if we were uploading pics
      hoverClass: 'ui-state-hover',
      focusClass: 'ui-state-focus',
      disabledClass: 'ui-state-disabled',   
      onSubmit: function(filename, extension) {
          // Create the elements of our progress bar
          var progress = document.createElement('div'), // container for progress bar
              bar = document.createElement('div'), // actual progress bar
              fileSize = document.createElement('div'), // container for upload file size
              wrapper = document.createElement('div'), // container for this progress bar
              progressBox = document.getElementById('progressBox'); // on page container for progress bars

          // Assign each element its corresponding class
          progress.className = 'progress';
          bar.className = 'bar';            
          fileSize.className = 'size';
          wrapper.className = 'wrapper';

          // Assemble the progress bar and add it to the page
          progress.appendChild(bar); 
          wrapper.innerHTML = '<div class="name">'+filename+'</div>'; // filename is passed to onSubmit()
          wrapper.appendChild(fileSize);
          wrapper.appendChild(progress);                                       
          progressBox.appendChild(wrapper); // just an element on the page to hold the progress bars    

          // Assign roles to the elements of the progress bar
          this.setProgressBar(bar); // will serve as the actual progress bar
          this.setFileSizeBox(fileSize); // display file size beside progress bar
          this.setProgressContainer(wrapper); // designate the containing div to be removed after upload
        },

       // Do something after finishing the upload
       // Note that the progress bar will be automatically removed upon completion because everything 
       // is encased in the "wrapper", which was designated to be removed with setProgressContainer() 
      onComplete:   function(filename, response) {
          if (!response) {
            alert(filename + 'upload failed');
            return false;
          }
          // Stuff to do after finishing an upload...
        }
});

For multiple file uploads, we use an additional function: setProgressContainer(elem). This function designates an element to be removed from the DOM after the upload is completed.

In the example, the element set to be removed with setProgressContainer() is the outer container for the progress elements. As a result, progress bars will be removed from the DOM after each upload is completed.

Cross-Browser Progress Support - How it Works

Because the progress event is not supported by Internet Explorer 9 (and older), progress updates must be retrieved from the server in order to provide progress bars in those browsers. The plugin includes optional, built-in support for handling this.

When the plugin detects support for the File API, the progress event is used. For older browsers (i.e., IE9 and below), the plugin will instead retrieve progress updates from the server, which are provided by uploadProgress.php (PHP w/ APC extension required - instructions below).

In both cases, everything is handled internally - feature detection, calculation, key handling, etc. To enable this behavior, just provide the URL for uploadProgress.php in the progressUrl option.

Installing the APC extension

The optional support for server-provided progress updates requires PHP with the APC extension installed and the apc.rfc1867 option enabled. To install APC:

sudo pecl install apc

Accept the default settings, and then create a configuration file:

sudo vi /etc/php.d/apc.ini

Add these two lines, and then save:

extension=apc.so
apc.rfc1867 = 1

Restart your web server for the changes to take effect.

Note: If APC is already installed, you may still need to add apc.rfc1867 = 1 to apc.ini, as it is not enabled by default.

Cross-Browser Helper Functions

To ease the pain of supporting older browsers, the plugin includes a set of callback functions which allow specific behavior to be defined based on whether the user's browser supports XHR uploads/HTML5 File API:

startXHR(filename, fileSize) - Called prior to upload -- only in browsers that support XHR uploads
endXHR(filename) - Called after upload is completed -- only in browsers that support XHR uploads
startNonXHR(filename) - Called prior to upload -- only in browsers that do not support XHR uploads
endNonXHR(filename) - Called after upload is completed -- only in browsers that do not support XHR uploads

A common use case is to show an upload progress bar in browsers that support the progress event while displaying an animated GIF in older browsers:

var progress = document.getElementById('progress'), // progress bar
    loaderImg = document.getElementById('loaderImg');  // "loading" animated GIF

var uploader = new ss.SimpleUpload({
      button: 'uploadButton',
      url: 'uploadHandler.php', // server side handler
      responseType: 'json',
      name: 'uploadfile',
      hoverClass: 'ui-state-hover',
      focusClass: 'ui-state-focus',
      disabledClass: 'ui-state-disabled',
      startXHR: function(filename, size) {                   
          progress.style.display = 'inline-block'; // show progress bar            
          this.setProgressBar(progress); // designate as progress bar
      },
      endXHR: function(filename) {
          progress.style.display = 'none'; // hide progress bar
      },
      startNonXHR: function(filename) {
          loaderImg.style.display = 'inline-block'; // show animated GIF
      },
      endNonXHR: function(filename) {
          loaderImg.style.display = 'none'; // hide animated GIF
      }
});

Returning false from startXHR() and startNonXHR() will prevent the upload from starting, just as it does with onSubmit() and onChange().

Using Uploader.php

Note: This PHP class is included only for convenience. It is not required to use PHP with Simple Ajax Uploader. The plugin is agnostic to server configuration, so use any language you prefer.

<?php
require('Uploader.php');

$upload_dir = '/img_uploads/';
$valid_extensions = array('gif', 'png', 'jpeg', 'jpg');

$Upload = new FileUpload('uploadfile');
$result = $Upload->handleUpload($upload_dir, $valid_extensions);

if (!$result) {
    echo json_encode(array('success' => false, 'msg' => $Upload->getErrorMsg()));   
} else {
    echo json_encode(array('success' => true, 'file' => $Upload->getFileName()));
}

You can also save the uploaded file with a different name by setting the newFileName property:

$Upload = new FileUpload('uploadfile');
$ext = $Upload->getExtension(); // Get the extension of the uploaded file
$Upload->newFileName = 'customFileName.'.$ext;
$result = $Upload->handleUpload($upload_dir, $valid_extensions);

To access the newly uploaded file, use the getSavedFile() method to get the file's path after the upload is completed:

$Upload = new FileUpload('uploadfile');
$result = $Upload->handleUpload($upload_dir, $valid_extensions);

if ($result) {
  $path = $Upload->getSavedFile();
  $imgsize = getimagesize($path);
  // image resizing stuff...
}

API Reference - Settings

Name Type Description
button
Default: ''
String, Element File upload button. Required.
url
Default: ''
String Location of the server-side file upload handler. Required.
name
Default: ''
String Upload parameter name. Required.
progressUrl
Default: ''
String Set to the location of uploadProgress.php (included) to enable cross-browser upload progress tracking (see example above).
multiple
Default: false
Boolean Set to true to enable multiple, concurrent file uploads.
maxUploads
Default: 3
Integer Max number of simultaneous uploads. If the queue option is true (default), files selected after the limit is reached will be queued and then automatically uploaded as prior uploads are completed.
maxSize
Default: false
Integer Maximum allowed file size (in kilobytes). Only works in browsers that support File API.
allowedExtensions
Default: []
Array Only allow file uploading for these extensions (case insensitive). Ex: allowedExtensions: ['jpg', 'jpeg', 'png', 'gif']
accept
Default: ''
String Sets the value of the accept file input attribute in supporting browsers.
queue
Default: true
Boolean If upload limit is reached, allow any files selected afterward to be queued and then automatically uploaded as prior uploads are completed.
data
Default: {}
Object Additional data to be sent to the server.
multipart
Default: false
Boolean Set to true for all files to be uploaded using multipart form method instead of direct binary stream.
method
Default: POST
String The HTTP method to use for XHR uploads.

Note: Only the POST method can be used in older browsers which rely on the iframe upload method.
autoSubmit
Default: true
Boolean By default, uploads commence as soon as a file is selected. Set to false to delay the upload and instead trigger manually with the submit() function.
responseType
Default: ''
String The type of data you're expecting back from the server. Default is plain text. Additional option is 'json'.
debug
Default: false
Boolean Set to true to log progress messages and server response in the console.
hoverClass
Default: ''
String Class applied to upload button when mouse is hovered.
focusClass
Default: ''
String Class applied to upload button when focused.
disabledClass
Default: ''
String Class applied to button when disabled.

API Reference - Callback Functions

Note: When returning false from a callback to prevent an upload, the current file will remain in the queue as the next to be uploaded. To remove the current file while in a callback, use: this.removeCurrent();

Name Arguments Description
onChange(filename, extension) filename (String), extension (String) Function to be called when user selects a file. The function gets passed two arguments: a string containing the filename; a string containing the file extension.

Return false to prevent the upload from starting.
onSubmit(filename, extension) filename (String), extension (String) Function to be called before file is uploaded. The function gets passed two arguments: a string containing the filename; a string containing the file extension.

Return false to prevent the upload from starting.
onProgress(pct) pct (Integer) Function to be called on the progress event in browsers that support XHR uploads. In older browsers, it will be called when server progress updates are received if progressURL is defined. The function gets passed one argument: an integer representing the upload completion percentage.
onComplete(filename, response) filename (String), response (Mixed) Function to be called when the upload is completed. The function gets passed two parameters: a string containing the filename; the data returned from the server, formatted according to the responseType setting. If responseType is 'json', the response will be evaluated as JSON and will return a Javascript object.
onExtError(filename, extension) filename (String), extension (String) Function to be called if the extension of a file is not permitted in the allowedExtensions option, if it is set. The function gets passsed two parameters: a string containing the filename; a string containing the file extension.

Note: The disallowed file is removed from the queue before onExtError() is called.
onSizeError(filename, fileSize) filename (String), fileSize (Integer) Function to be called if the file size exceeds the limit which is set in the maxSize option, if it is set. The function gets passsed two parameters: a string containing the filename; an integer representing the file size.

Note: The disallowed file is removed from the queue before onSizeError() is called.
onError(filename, errorType, response) filename (String), errorType (String), response (String) Function to be called if an error occurs during upload. The function gets passsed three parameters: a string containing the filename; a string containing the error type; a string containing the server response, if any.
startXHR(filename, fileSize) filename (String), fileSize (Integer) Function to be called only in browsers that support XHR uploads. Called after onSubmit() but prior to upload start. The function gets passed two arguments: a string containing the filename; a number that is the file size in kilobytes.

Return false to prevent the upload from starting.
endXHR(filename) filename (String) Function to be called only in browsers that support XHR uploads. Called after upload is completed but prior to onComplete(). The function gets passed one argument: a string containing the filename.
startNonXHR(filename) filename (String) Function to be called only in browsers that do not support XHR uploads (IE9 and older). Called after onSubmit() but prior to upload start. The function gets passed one argument: a string containing the filename.

Return false to prevent the upload from starting.
endNonXHR(filename) filename (String) Function to be called only in browsers that do not support XHR uploads (IE9 and older). Called after upload is completed but prior to onComplete(). The function gets passed one argument: a string containing the filename.

API Reference - Cross-Browser Progress Utilities

Note: The following items are only applicable if the progressUrl option is set to the URL of uploadProgress.php (see above for setup instructions).

Name Type Description
checkProgressInterval
Default: 50
Integer Length of delay (in milliseconds) between completed progress update checks.
keyParamName
Default: APC_UPLOAD_PROGRESS
String The name specified in PHP configuration to activate APC upload progress. This is the value of apc.rfc1867_name in PHP runtime config. (PHP default value is "APC_UPLOAD_PROGRESS")
More info at php.net
onUpdateFileSize(filesize) Function This callback function serves the specific purpose of providing the upload file size in browsers that do not support the HTML5 File API. It is called after the first progress update. The function gets passed one argument: the size (in KB) of the uploaded file.

API Reference - Instance Methods

Note: See the examples above for instructions on how to use setProgressBar(), setFileSizeBox(), and setProgressContainer().

Name Parameters Description
submit() none Begins the file upload process. Note that if autoSubmit is set to true (the default value), there is no need to manually call submit(). The upload process will begin immediately after the user selects a file.
setData(data) data (Object) Replaces the data to be sent to the server. Note that all previously set data is entirely removed and replaced.
disable() none Disables upload functionality.
enable() none Restores upload functionality.
getQueueSize() none Returns number of files currently waiting in queue.
removeCurrent() none Remove the currently active file from the queue. Must be called prior to the start of upload (for example, within onSubmit() or onChange()).
setProgressBar(element) element (Element) Designates an element to serve as an upload progress bar. The CSS width of the element will be set to 0% at the start of the upload, and then updated accordingly by percentage as the upload progresses.
setFileSizeBox(element) element (Element) Designates an element as the container in which the file size of an uploading file will be inserted upon the start of the upload.
setProgressContainer(element) element (Element) Designates an element to be removed from the DOM upon completion of an upload. Useful for cleaning up dynamically created progress bars.

License

Released under the MIT license.