Version 3, last updated by Diego Medina at 05 Feb 17:36 UTC

There is also this example of ajax upload using lift and MongoDB

Ajax Upload with drag and drop

Regular AJAX calls (using the XmlHttpRequest object) have not traditionally supported file upload. Recent changes to the specifications have been implemented in the newest browsers, but most browsers in the wild still don’t support it. Fortunately, there’s an old technique that can be used to transparently change a Lift ajaxForm with a file upload component to work properly without having to deal with Flash or any other techniques.

A full example implementing this technique is at https://github.com/Shadowfiend/lift-ajax-file-upload-example . A similar technique, with file upload feedback based on Lift’s comet support and the jquery file upload plugin, is at https://github.com/mbk/ajaxupload_with_comet .

Technique

Before the XmlHttpRequest object was common or implemented anywhere other than Internet Explorer, similar functionality was accomplished by using hidden iframes to load content and then acting on that content, thus avoiding a page reload. Forms could be submitted in this way by setting up a hidden iframe and setting the form `target’ attribute to that iframe.

While we now have AJAX as a first-class citizen and almost all browsers in the wild, AJAX file upload isn’t supported in most of those browsers. Thus, we turn back to this old technique.

Implementation

Implementation is straightforward: for all aspects of the server-side and HTML markup, write your template as you usually would for an AJAX form, and use the fileUpload Lift helper when appropriate.

Afterwards, you can apply a small jQuery script to iframe the form:


$(document).ready(function() {
  function submitFormToIframe($form, target) {
    $form
      .attr('target', target)
      .removeAttr('onsubmit')
      .attr('action', '/ajax_request/' + lift_page)
      .attr('method', 'post')
      .attr('enctype', 'multipart/form-data' )    
      .attr('encoding', 'multipart/form-data')
      .find('input:submit,button[type=submit]')
        .removeAttr('onclick')
      .end()
      .append($('<input type="hidden" name="' +
                  $form.find('input:submit').attr('name') +
                  '" value="_" />'))
      .after(
        // do not use attr() to set name. IE7 will hate this: http://stackoverflow.com/questions/2105815/weird-behaviour-of-iframe-name-attribute-set-by-jquery-in-ie
        $('<iframe id="' + target + '" name="' + target + '" />')
          .addClass('form-target')
          .css('display','none')
          .load(function() {
              $.globalEval($(this).contents().text());
          })
      );
  }

  submitFormToIframe($('form:has(input[type=file])'), 'fileUploadExampleForm');
});

And that’s it! The form will now submit to an iframe, and the resulting code will be eval’ed when the iframe finishes loading (i.e., when Lift responds with your JsCmds).

Internet Explorer

Naturally IE requires an extra backflip to make things work. Lift sends JavaScriptResponses correctly as MIME type text/javascript. Unfortunately, if IE tries to load text/javascript in an iframe, it will instead offer to download it (save or open with). This obviously breaks all of our hard work above. We fix this by converting the MIME type to text/plain if we have an IE JavaScriptResponse with an uploaded file, done in Boot.scala using a responseTransformer:


    LiftRules.responseTransformers.append {
      resp =>
        (for (req <- S.request) yield {
          resp match {
            case InMemoryResponse(data, headers, cookies, code)
                                    if ! req.uploadedFiles.isEmpty &&
                                        req.isIE &&
                                        req.path.wholePath.head == LiftRules.ajaxPath =>
              val contentlessHeaders = headers.filterNot(_._1.toLowerCase == "content-type")
              InMemoryResponse(data, ("Content-Type", "text/plain; charset=utf-8") :: contentlessHeaders, cookies, code)
            case _ => resp
          }
        }) openOr resp
    }

Examples

Again, all of these changes are in context in a Lift example at https://github.com/Shadowfiend/lift-ajax-file-upload-example .

Additionally, an example with progress done via Lift’s comet support is at https://github.com/mbk/ajaxupload_with_comet .