I recently started working on a multi-file upload HTTP page for a customer.
He wants it all in HTTP, Javascript and HTML, needs to be able to transmit large files (up to 800 megabytes each), and he also wants a progressbar for the uploads. Flash or Silverlight is not an accepted option and finally, third-party components are not accepted either, since tight customizability deep down is needed without too much hassle.
Now, I get the whole concept. You have a main page which has an iframe (ewww, I said the i-word – I know). This iframe points to a form which has the file upload in it. This way, once this form is submitted, the original page that the visitor is on won’t reload, allowing the original page to run some AJAX, constantly asking what the progress of the transfer is.
First attempts
I first started out fetching a file in MVC by declaring it directly as a parameter to the action method, as shown below.
public ActionResult UploadFile(HttpPostedFile upload) {
...
}
But as great as this help MVC is giving me is, neither the InputStream property or the SaveAs(...) method on the upload variable helped me.
BeginExecute and GetBufferedInputStream
Quickly I found a method introduced in .NET 4.0 called HttpRequest.GetBufferedInputStream(). This method in particular allowed me to receive the file-stream and read it incrementally and asynchronously.
I overwrote the BeginExecute method of my controller, and put the reading functionality in there, when a specific URL was triggered, like this.
protected override IAsyncResult BeginExecute(System.Web.Routing.RequestContext requestContext, AsyncCallback callback, object state)
{
if (requestContext.HttpContext.Request.Url.AbsolutePath.StartsWith("/Home/UploadFile"))
{
//we're doing a file upload
var streamSource = requestContext.HttpContext.Request.GetBufferedInputStream();
//read from stream, write to file
}
}
This all works just fine… When AJAX doesn’t go in and interrupt. You see, if I have my AJAX script running (which executes 10 seconds after the iframe upload has started), then (when it makes its GET request) that request interrupts the stream that is reading the file, even though it is on a different action, and even when it is on a different controller.
The error I am receiving is the following:
The client is disconnected because the underlying request has been completed. There is no longer an HttpContext available.
What can I do about this?
Take a look at ASP .NET Ajax File Upload. This should clarify how the GetBufferlessInputStream and GetBufferedInputStream works and how you can use them for an “ajax” file upload, but will force you to change a lot of code.
If you are using .NET 4.5, an updated version of the project that uses GetBufferedInputStream will come soon and it will be easy to use. You can check the same link for updates.