jQuery File Upload: IE9 and ASP.Net Web API File Uploading
The blueimp jQuery File Upload plugin uses the XMLHttpRequest to pass the file data to a server (only IE10+). If browser doesn’t support Ajax file uploading, the plugin makes a workaround by dynamically creating IFrame and sending the data on behalf of it through the traditional form POST. The JavaScript responsible for the workaround resides in jquery.iframe-transport.js, which accompanies the basic jquery.fileupload.js. The following TypeScript code could be used to initialize the plugin and submit file data (the code is intentionally kept as simple as possible – no progress bars, validations and so on):
//...
private fileData: any = {};
//...
$(".filePicker").fileupload({
autoUpload: false, // will be submitted once button is clicked
method: "PUT",
dataType: "json",
url: "api/someController/someMethod", // Web Api method to receive and process the file
formData: () => { // additional parameters accompanying the file data
return [{
name: "bookName",
value: $("#bookName").val()
},
{
name: "bookGenre",
value: $("#bookGenre").val()
},
{
name: "bookAuthor",
value: $("#bookAuthor").val()
}];
},
add: (e: JQueryEventObject, data: any) => { // event handlers
this.fileData = data;
//...
},
done: (e: JQueryEventObject, data: any) => {
//...
},
fail: (e: JQueryEventObject, data: any) => {
//...
},
always: (e: JQueryEventObject, data: any) => {
//...
}
});
//...
$("#loadBook").on('click', function () { // the button to initiate the file sending
if (this.fileData && this.fileData.hasOwnProperty("process")) {
// file data validation: size, extension, whatever else...
this.fileData.process().done(() => { // file sending
this.fileData.submit();
});
}
});
On the server side the following code receives and processes the file data:
using System;
using System.Text;
using System·Web;
using System·Web.Http;
using System.Net;
using System.Net.Http;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Linq;
...
namespace DotNetFollower.Web.Controllers.Api
{
[RoutePrefix("api/someController")]
public class someController : ApiController
{
public someController()
{
//...
}
[Route("someMethod")]
[HttpPut]
[HttpPost] // this attribute allows processing traditional form POST
public ServiceResult<BookOutput> someMethod()
{
try
{
//...
var request = HttpContext.Current.Request;
var files = request.Files;
if (files.Count == 0)
throw new Exception("Couldn't find a book to load!");
// read accompanying parameters
var bookName = request.Form.Get("bookName");
var bookGenre = request.Form.Get("bookGenre");
var bookAuthor = request.Form.Get("bookAuthor");
var file = new HttpPostedFileWrapper(files[0]);
// parsing file.InputStream ...
// processing the parsed file data ...
file.InputStream.Close();
//...
return new ServiceResult<BookOutput>()
{
Data = new BookOutput()
{
Name = bookName,
Genre = bookGenre,
Author = bookAuthor
}
};
}
catch(Exception ex)
{
return new ServiceResult<BookOutput>(ex);
}
}
}
}
// Where ServiceResult and BookOutput are defined as follows
public class ServiceResult<T>
{
public bool Success { get; set; }
public string ErrorMessage { get; set; }
public T Data { get; set; }
public ServiceResult()
{
Success = true;
}
public ServiceResult(string errorMessage)
{
ErrorMessage = errorMessage;
}
public ServiceResult(Exception exception)
{
ErrorMessage = exception.Message;
}
}
public class BookOutput
{
public string Name { get; set; }
public string Genre { get; set; }
public string Author { get; set; }
}
Unfortunately, the code doesn’t works as expected in Internet Explorer 9 (thankfully, the lower versions are not supposed to be supported by the project, so I don’t care about them). If IE9 sends an Ajax request to the Web Api method, it interprets the JSON response correctly. However, when uploading a file, the jQuery File Upload plugin sends traditional non-Ajax form POST. So, having received the JSON result, IE9 prompts for a JSON file download.
![]()
To bypass such IE9 behaviour the server response should contain the content-type header “text/html” rather than the “application/json” returned by the Web Api method by default. To avoid writing some IE9 specific logic on both server and client sides, I’ve introduced the following Web Api method-adapter:
[Route("someMethodAdapted")]
[HttpPut]
[HttpPost] // this attribute allows processing traditional form POST
public HttpResponseMessage someMethodAdapted()
{
var res = someMethod(); // call the original method
const string JsonContentType = "application/json";
const string HtmlContentType = "text/html";
var response = Request.CreateResponse(HttpStatusCode.OK); // return 200 OK
var context = HttpContext.Current;
response.Content = new StringContent(ToJson(res), // serialize result object into JSON
Encoding.UTF8,
// check if the JSON content-type is accepted
// (it's not accepted in case of form POST coming from IE9)
context.Request.AcceptTypes.Contains(JsonContentType, StringComparer.OrdinalIgnoreCase) ?
JsonContentType : HtmlContentType); // return suitable content-type
return response;
}
// the ToJson method is defined as follows
private static string ToJson<T>(T obj) where T : class
{
if (obj == null)
return string.Empty;
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
using (MemoryStream stream = new MemoryStream())
{
serializer.WriteObject(stream, obj);
return Encoding.Default.GetString(stream.ToArray());
}
}
The someMethodAdapted is supposed to be used instead of someMethod everywhere on the client side. So, repoint the url to the method-adapter
...
$(".filePicker").fileupload({
...
// Web Api method-adapter to receive and process the file
url: "api/someController/someMethodAdapted",
...
});
...
The someMethodAdapted makes a content-type trick and perfectly serves IE9 and higher. The use of HttpResponseMessage gives a control over the response headers. The HttpContext.Current.Request.AcceptTypes is a list of client-supported content types (aka MIME types). If the “application/json” is not in the list, the “text/html” is the right choice. Below are the AcceptTypes of Ajax and non-Ajax requests made by IE9:
// IE9 Ajax request to a Web Api method (true for higher browser versions too)
HttpContext.Current.Request.AcceptTypes {string[3]} string[]
[0] "application/json" string
[1] "text/javascript" string
[2] "*/*; q=0.01" string
// IE9 non-Ajax form POST
HttpContext.Current.Request.AcceptTypes {string[3]} string[]
[0] "text/html" string
[1] "application/xhtml+xml" string
[2] "*/*" string

