Sunday, November 28, 2010

AJAX and File Upload — Do They Go Together

In the purest sense, AJAX does not support file upload. Two main reasons:
  1. Cannot specify form data encoding type (enctype) in AJAX form
  2. The method in the form tag is not really executed

But what is the idea of AJAX? It is retrieving content from the web server without refreshing the web page. In the case of a file upload, the browser sends a file to the server and retrieve some sort of acknowledgement of the upload. So, is it possible to perform a file upload without refreshing the webpage?

Most of the work around solutions on the Internet make use of an iframe. One of the nicer work around solutions found is by webtoolkit. It has both pre and post function calls. So, actions can be done before the upload and after the upload has been completed. The code below is an adaptation of that solution.

One of the most outrageous work around is a hack in Firefox. It involves modifying one of Firefox's internal setting variables (signed.applets.codebase_principal_support). Yeah, Not a good idea as it is browser dependent and potentially breaks down in future versions.



JavaScript for AJAX like File Upload
// JavaScript Document

// Author: Luke Lau

// This JavaScript is based on AJAX iFrame Method written by
// webtoolkit (http://www.webtoolkit.info/ajax-file-upload.html)


function AIMonStartCallback () {
  // do something useful before submit (onStart)

  return true;
} // function AIMonStartCallback



function AIMonCompleteCallback (response) {
  // do something useful after submit (onComplete)
  document.getElementById('imageLibraryAJAXContentContainer').innerHTML = response;

  // unregister event listeners because content has changed on the page
  registerImageLibraryEvents(false);

  // re-register event listeners based on new content on the page
  registerImageLibraryEvents(true);
} // function AIMonCompleteCallback



// class definition
AIM = {
  frame : function (AIMcallback) {
    // 1: start of creating an iframe
    // give the iframe an unique id
    var AIMiframeId = 'AIMiframe' + Math.floor(Math.random() * 99999);

    document.getElementById('AIMiframeContainer').innerHTML = '<iframe 
        style="display: none" width="100%" 
        src="about:blank" id="' + AIMiframeId + '" name="' + AIMiframeId + '" 
        onload="AIM.loaded(\'' + AIMiframeId + '\')"></iframe>';
    // end of creating an iframe

    // 2: start of set up the listener / handler for the newly 
    //    created iframe's oncomplete callback
    var AIMiframe = document.getElementById(AIMiframeId);

    if (AIMcallback && typeof(AIMcallback.onComplete) == 'function') {
      AIMiframe.onComplete = AIMcallback.onComplete;
    }
    // end of set up the listener / handler for the iframe's oncomplete
    // callback return the new iframe's id
    return AIMiframeId;
  }, // frame


  form : function (AIMform, AIMiframeId) {
    // modify the target attribute of the form to the newly created
    // iframe (referenced by id)
    AIMform.setAttribute('target', AIMiframeId);
  }, // form


  submit : function (AIMform, AIMcallback) {
    // 1: set up the form just before it starts
    AIM.form(AIMform, AIM.frame(AIMcallback));

    // 2: start of set up the listener / handler for the newly created
    //    iframe's onstart callback
    if (AIMcallback && typeof(AIMcallback.onStart) == 'function') {
      return AIMcallback.onStart();
    } else {
      return true;
    }
  }, // submit


  loaded : function (AIMiframeId) {
    var AIMiframe = document.getElementById(AIMiframeId);

    if (AIMiframe.contentDocument) {
      var AIMiframeDocument = AIMiframe.contentDocument;
    } else if (AIMiframe.contentWindow) {
      var AIMiframeDocument = AIMiframe.contentWindow.document;
    } else {
      var AIMiframeDocument = window.frames[AIMiframeId].document;
    }

    if (AIMiframeDocument.location.href == 'about:blank') {
      return;
    }

    if (typeof(AIMiframe.onComplete) == 'function') {
      AIMiframe.onComplete(AIMiframeDocument.body.innerHTML);
    }
  } // loaded
} // AIM