Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File uploads via Web Api fail on second upload

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>&lt;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.

like image 380
John Livermore Avatar asked Apr 24 '26 18:04

John Livermore


2 Answers

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"));
   }
like image 162
Filip W Avatar answered Apr 26 '26 09:04

Filip W


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)

like image 27
tpeczek Avatar answered Apr 26 '26 08:04

tpeczek