jQuery File Upload in ASP.NET MVC without using Flash

In this post, I will show how to implement jQuery File Upload in ASP.NET MVC without using any Flash components.


Why bother? Why not just use Flash?

That was my first thought. So I started down the path of using SWFUpload. It did not take me long to realize that I would run into a well known bug where non-IE browsers will not pass cookies properly. There are several fixes available that require you to hack the server side code. This worked fine for things like ASP.NET session state, forms authentication cookie, etc, but this project needed to work in conjunction with an single sign-on ISAPI filter that I could not modify. Since users of this site needed to be authenticated by the corporate ISAPI filter, I was unable to use Flash.


So what to use if Flash is not an option?

I googled around a bit, and found several plug-ins. But I did not really want to import yet another script library into my already complicated page. Since I was already using the jQuery Form Plugin for other features, the choice was easy.

Use jQuery Form Plugin

From the jQuery Form Plugin website, some important information about how to get a JsonResult back to the browser:

Since it is not possible to upload files using the browser's XMLHttpRequest object, the Form Plugin uses a hidden iframe element to help with the task. This is a common technique, but it has inherent limitations. The iframe element is used as the target of the form's submit operation which means that the server response is written to the iframe. This is fine if the response type is HTML or XML[1], but doesn't work as well if the response type is script or JSON, both of which often contain characters that need to be represented using entity references when found in HTML markup.

To account for the challenges of script and JSON responses, the Form Plugin allows these responses to be embedded in a textarea element and it is recommended that you do so for these response types when used in conjuction with file uploads. Please note, however, that if a file has not been selected by the user for the file input then the request uses normal XHR to submit the form (not an iframe). This puts the burden on your server code to know when to use a textarea and when not to. If you like, you can use the iframe option of the plugin to force it to always use an iframe mode and then your server can always embed the response in a textarea. The following response shows how a script should be returned from the server:

The easiest way to do this in ASP.NET MVC, is to create a new class that inherits from JsonResult:

   1: public class FileUploadJsonResult : JsonResult
   2: {
   3:     public override void ExecuteResult(ControllerContext context)
   4:     {
   5:         this.ContentType = "text/html";
   6:         context.HttpContext.Response.Write("<textarea>");
   7:         base.ExecuteResult(context);
   8:         context.HttpContext.Response.Write("</textarea>");
   9:     }
  10: }


And here is how to code your controller method:

   1: public FileUploadJsonResult AjaxUpload(HttpPostedFileBase file)
   2: {
   3:     // TODO: Add your business logic here and/or save the file
   5:     // Return JSON
   6:     return new FileUploadJsonResult { Data = new { message = string.Format("{0} uploaded successfully.", System.IO.Path.GetFileName(file.FileName)) } };
   7: }

And here is the view:

   1: <script type="text/javascript">
   2:     $(function() {
   4:         $("#ajaxUploadForm").ajaxForm({
   5:             iframe: true,
   6:             dataType: "json",
   7:             beforeSubmit: function() {
   8:                 $("#ajaxUploadForm").block({ message: '<h1><img src="/Content/busy.gif" /> Uploading file...</h1>' });
   9:             },
  10:             success: function(result) {
  11:                 $("#ajaxUploadForm").unblock();
  12:                 $("#ajaxUploadForm").resetForm();
  13:                 $.growlUI(null, result.message);
  14:             },
  15:             error: function(xhr, textStatus, errorThrown) {
  16:                 $("#ajaxUploadForm").unblock();
  17:                 $("#ajaxUploadForm").resetForm();
  18:                 $.growlUI(null, 'Error uploading file');
  19:             }
  20:         });
  21:     });
   3: <form id="ajaxUploadForm" action="<%= Url.Action("AjaxUpload", "Upload")%>" method="post" enctype="multipart/form-data">
   4:     <fieldset>
   5:         <legend>Upload a file</legend>
   6:         <label>File to Upload: <input type="file" name="file" />(100MB max size)</label>
   7:         <input id="ajaxUploadButton" type="submit" value="Submit" />
   8:         </fieldset>
   9: </form>

You can download a the full source project from here.


#1 Tomek M. on Tuesday, August 25 2009 at 9:09 AM