Ajax file upload methods without Flash 

Joined:
02/21/2009
Posts:
130

June 30, 2010 19:04:45    Last update: July 03, 2010 21:24:33
Technically, file upload cannot be handled by Ajax, because XMLHttpRequest (XHR) does not handle file inputs. All techniques not using Flash rely on an invisible iframe as the upload form submit target. JavaScript then grabs the response content from the iframe and present it, giving the same illusion as Ajax.
  1. webtoolkit AIM
    The technique by webtoolkit is very simple. It involves 3 simple steps: include the AIM script, implement the start/finish JavaScript functions, and add an onsubmit handler to the normal file upload form.

    The hooked up form looks like:
    <head>
    <script type="text/javascript" src="webtoolkit.aim.js"></script>
    <script type="text/javascript">
        function startCallback() {
    	// make something useful before submit (onStart)
    	return true;
        }
    
        function completeCallback(response) {
    	// make something useful after (onComplete)
    	// response is the innerHTML of the iframe
        }
    </script>
    </head>
     
    <body>
        <form action="handleUpload.do"
    	  method="POST"
    	  enctype="multipart/form-data"
    	  onsubmit="return AIM.submit(this,
    			{
    			    'onStart': startCallback, 
    			    'onComplete': completeCallback
    			}
    		    )">
    <div><label>Name:</label> <input type="text" name="form[name]" /></div>
    <div><label>File:</label> <input type="file" name="form[file]" /></div>
    <div><input type="submit" value="SUBMIT" /></div>
    </form>
    </body>
    


    The AIM script is also quite simple:
    /**
    *
    *  AJAX IFRAME METHOD (AIM)
    *  http://www.webtoolkit.info/
    *
    **/
     
    AIM = {
    	frame : function(c) {
    		var n = 'f' + Math.floor(Math.random() * 99999);
    		var d = document.createElement('DIV');
    		d.innerHTML = '<iframe style="display:none" '
    			    + 'src="about:blank" id="'
    			    + n + '" name="'
    			    + n + '" onload="AIM.loaded(\''
    			    + n + '\')"></iframe>';
    		document.body.appendChild(d);
     
    		var i = document.getElementById(n);
    		if (c && typeof(c.onComplete) == 'function') {
    			i.onComplete = c.onComplete;
    		}
     
    		return n;
    	},
     
    	form : function(f, name) {
    		f.setAttribute('target', name);
    	},
     
    	submit : function(f, c) {
    		AIM.form(f, AIM.frame(c));
    		if (c && typeof(c.onStart) == 'function') {
    			return c.onStart();
    		} else {
    			return true;
    		}
    	},
     
    	loaded : function(id) {
    		var i = document.getElementById(id);
    		if (i.contentDocument) {
    			var d = i.contentDocument;
    		} else if (i.contentWindow) {
    			var d = i.contentWindow.document;
    		} else {
    			var d = window.frames[id].document;
    		}
    		if (d.location.href == "about:blank") {
    			return;
    		}
     
    		if (typeof(i.onComplete) == 'function') {
    			i.onComplete(d.body.innerHTML);
    		}
    	}
    }
    


    The above code only supports HTML responses. In order to support JSON responses, the above needs to be modified to add a responseType field.
    frame : function(c) {
    	var n = 'f' + Math.floor(Math.random() * 99999);
    	var d = document.createElement('DIV');
    	d.innerHTML = '<iframe style="display:none" src="about:blank" id="'+n+'" name="'+n+'" onload="AIM.loaded(\''+n+'\')"></iframe>';
    	document.body.appendChild(d);
     
    	var i = document.getElementById(n);
    	if (c && typeof(c.onComplete) == 'function') {
    		i.onComplete = c.onComplete;
    	}
    
    	i.responseType = c.responseType;
    	return n;
    },
    
    loaded : function(id) {
    	var i = document.getElementById(id);
    	if (i.contentDocument) {
    		var d = i.contentDocument;
    	} else if (i.contentWindow) {
    		var d = i.contentWindow.document;
    	} else {
    		var d = window.frames[id].document;
    	}
    	if (d.location.href == "about:blank") {
    		return;
    	}
    
    	var response = d.body.innerHTML;
    	if (i.responseType.toLowerCase() == 'json') {
    	    if (d.body.firstChild && d.body.firstChild.nodeName.toUpperCase() == 'PRE') {
    		response = d.body.firstChild.firstChild.nodeValue;
    	    }
    	    response = eval('(' + response + ')');
    	}
     
    	if (typeof(i.onComplete) == 'function') {
    	    i.onComplete(response);
    	}
    }
    


    The onsubmit handler will need to be changed to:
    <form action="handleUpload.do"
    	  method="POST"
    	  enctype="multipart/form-data"
    	  onsubmit="return AIM.submit(this,
    			{
    			    'onStart': startCallback, 
    			    'onComplete': completeCallback,
    			    'responseType': 'json'
    			}
    		    )">
    


    Due to IE quirks, I was not able to add XML support.

  2. Andrew Valums Ajax Upload
    Valums Ajax upload is a lot more full-featured. It does not rely on an existing file upload form. Instead, you use it to turn a DOM element into an upload button. The script takes care of creating the upload form. In the simplest form:
    // turn DOM element with id upload_button_id into an upload button
    // the default name for the file input is 'userfile'.
    new AjaxUpload('upload_button_id', {action: 'uploadHandler.php'});
    


    Or, more elaborately:
    new AjaxUpload('upload_button_id', {
      // Location of the server-side upload script
      // NOTE: You are not allowed to upload files to another domain
      action: 'upload.php',
      
      // File upload name
      name: 'nameForFileInput',
      
      // Additional data to send
      data: {
        example_key1 : 'example_value',
        example_key2 : 'example_value2'
      },
      
      // Submit file after selection
      autoSubmit: true,
      
      // The type of data that you're expecting back from the server.
      // HTML (text) and XML are detected automatically.
      // Useful when you are using JSON data as a response, set to "json" in that case.
      // Also set server response type to text/html, otherwise it will not work in IE6
      responseType: false
    });
    


    Valums Ajax Upload allows you to style the file upload button any way you want. Since the file input element is basically un-scriptable, the trick is to overlay an invisible file input on top of your visible upload button, so that when you click on the upload button (or you think you are clicking on it), you actually clicked the invisible file input. Therefore, the file chooser dialog pops up.

    Note that if you want to send input fields other than the file input, you have to manually attach them with the data option or the setData method.

  3. phpletter.com jQuery Ajax File Upload Plugin
    This is wrapped as jQuery plugin. JavaScript is used to create the file upload form. It also supports JSON and xml responses in addition to HTML. But I don't think the design is as flexible as Valums Ajax Upload.
Share |
| Comment  | Tags