I am working with Web Api to create a way to upload files via web api. I have found several blog posts on how to accomplish this, and the code is all very similar with a key commonality being the Request.Content.ReadAsMultipartAsync() call. The problem I have is the first upload works fine, but then IIS gets into a faulted state where subsequent uploads fail. The first 32Kb comes in, but then it quits. Debugging shows only a null reference exception that occurs somewhere in the ASP.NET framework.
Here is the ApiController definition I have...
public class FileUploadController : ApiController
{
public void Post()
{
if (Request.Content.IsMimeMultipartContent())
{
var path = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(path);
var task = Request.Content.ReadAsMultipartAsync(provider);
task.ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
});
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
}
}
Also, here is the page I am posting from...
<!doctype html>
<head>
<title>File Upload Progress Demo #3</title>
</head>
<body>
<h1>File Upload Progress Demo #3</h1>
<code><input type="file" name="myfile[]"></code><br>
<form action="/api/fileupload" method="post" enctype="multipart/form-data">
<input type="file" name="myfile"><br>
<input type="submit" value="Upload File to Server">
</form>
<div class="progress">
<div class="bar"></div>
<div class="percent">0%</div>
</div>
<div id="status"></div>
</body>
The above code can be downloaded in a default WebApi solution from https://github.com/JohnLivermore/FileUploadTest. Run and navigate to http://localhost:{port}/FormPost.html. The first upload succeeds (uploads to App_Data), but subsequent uploads only upload the first 32 Kb and then fail.
You shouldn't use a void method.
Void and async don't play well together for a number of reasons.
public Task<HttpResponseMessage> Post()
{
var rootUrl = "c:/uploads";
if (Request.Content.IsMimeMultipartContent())
{
var streamProvider = new MultipartFormDataStreamProvider(rootUrl);
var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith<HttpResponseMessage>(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
//do stuff with files if you wish
return new HttpResponseMessage(HttpStatusCode.OK);
});
return task;
}
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
The main issue of your code is exiting your method without waiting for all asynchronous tasks to finish. You can use .Wait() for that purpose:
public class FileUploadController : ApiController
{
public void Post()
{
if (Request.Content.IsMimeMultipartContent())
{
var path = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(path);
var readAsMultipartTask = Request.Content.ReadAsMultipartAsync(provider);
var continueWithTask = readAsMultipartTask.ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
});
continueWithTask.Wait();
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
}
}
This will make the upload work properly, but you should be aware that your method is breaking HTTP protocol because you are not sending back any response in case of proper upload. Your method should be more like this:
public class FileUploadController : ApiController
{
public async Task<HttpResponseMessage> Post()
{
if (Request.Content.IsMimeMultipartContent())
{
var path = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(path);
await Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
});
//Here you should return a meaningful response
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
}
}
(Thanks to use of async/await the synchronization of asynchronous tasks is handled by framework)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With