我正在使用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><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然后失败。
答案 0 :(得分: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异步任务的同步由框架处理)
答案 1 :(得分:5)
您不应该使用void方法。
由于多种原因,虚空和异步不能很好地协同工作。
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"));
}