3

我正在使用 Web Api 创建一种通过 Web api 上传文件的方法。我找到了几篇关于如何完成此操作的博客文章,并且代码都非常相似,其中一个关键的共同点是 Request.Content.ReadAsMultipartAsync() 调用。我遇到的问题是第一次上传工作正常,但随后 IIS 进入故障状态,后续上传失败。第一个 32Kb 进入,但随后退出。调试仅显示在 ASP.NET 框架中某处发生的空引用异常。

这是我的 ApiController 定义...

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"));
        }
    }
}

另外,这是我发布的页面...

<!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>

上面的代码可以从https://github.com/JohnLivermore/FileUploadTest下载到默认的 WebApi 解决方案中。运行并导航到http://localhost:{port}/FormPost.html。第一次上传成功(上传到 App_Data),但后续上传只上传前 32 Kb,然后失败。

4

2 回答 2

5

您的代码的主要问题是在不等待所有异步任务完成的情况下退出您的方法。您可以.Wait()为此目的使用:

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"));
        }
    }
}

这将使上传正常工作,但您应该知道您的方法正在破坏 HTTP 协议,因为在正确上传的情况下您没有发回任何响应。你的方法应该更像这样:

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"));
        }
    }
}

(感谢使用 async/await 异步任务的同步由框架处理)

于 2012-10-12T20:08:32.757 回答
5

您不应该使用 void 方法。

由于多种原因,Void 和 async 不能很好地配合使用。

   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"));
   }
于 2012-10-16T20:36:27.433 回答