我的团队最近重构了一个Web API服务,将一些重复代码移动到静态方法中。一种方法涉及从请求中提取上载的文件。该方法适用于单元测试,但在负载下,会抛出异常。部分代码是在SO帖子中找到的,但我担心总的来说,我们没有正确使用它。这是代码:
internal static string ExtractFile(HttpRequestMessage request)
{
if (request.Content.IsMimeMultipartContent())
{
string uploadRoot = ServiceHelper.GetUploadDirectoryPath();
var provider = new MultipartFormDataStreamProvider(uploadRoot);
try
{
Task.Factory
.StartNew(() => provider = request.Content.ReadAsMultipartAsync(provider).Result,
CancellationToken.None,
TaskCreationOptions.LongRunning, // guarantees separate thread
TaskScheduler.Default)
.Wait();
}
catch(System.AggregateException ae)
{
if(log.IsErrorEnabled)
{
foreach(var ex in ae.InnerExceptions)
{
log.Error("ReadAsMultipartAsync task error.", ex);
}
}
var errorResponse = request.CreateErrorResponse(HttpStatusCode.InternalServerError, "An error occurred while extracting the uploaded file from the request.");
throw new HttpResponseException(errorResponse);
}
var fileData = provider.FileData.First();
var localName = fileData.LocalFileName;
var content = File.ReadAllText(localName);
if (log.IsDebugEnabled)
{
var embeddedName = fileData.Headers.ContentDisposition.FileName;
log.DebugFormat("File {0} was successfully uploaded as '{1}'.", embeddedName, localName);
}
return content;
}
else
{
log.Error("Invalid request received. Request must be in a multipart/form-data request.");
var errorResponse = request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Request must be a multipart/form-data request and contain one file.");
throw new HttpResponseException(errorResponse);
}
}
走过日志,我看到这样的错误:
System.IO.IOException:读取MIME多部分正文部分时出错。 ---> System.IO.IOException ---> System.Net.HttpListenerException:由于线程退出或应用程序请求,I / O操作已中止
HttpListenerRequest已经处理
System.IO.IOException:读取MIME多部分正文部分时出错。 ---> System.IO.IOException ---> System.Net.HttpListenerException:尝试对不存在的网络连接执行操作
此Web服务作为自托管的OWIN Windows服务运行。文件上传量很小(3k到4k)。
我无法通过一次上传重新创建该问题。与服务通信的客户端使用任务来发布文件,但它通常不会同时运行4个或5个以上的任务。我和我的团队对.NET任务相对较新。其中一个开发人员想知道TaskCreationOptions.LongRunning参数是否真的伤害了它的帮助。有什么建议吗?
更新
我尝试使用以下方法切换Task.Factory代码:
var task = Task.Run(async () => await request.Content.ReadAsMultipartAsync(provider));
task.Wait();
provider = task.Result;
我还有一些问题,但这似乎更好。不过不确定原因。
答案 0 :(得分:11)
我遇到了同样的问题,通过在web.config
中添加以下代码解决了这个问题<system.web>
<httpRuntime maxRequestLength="30000000" />
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="30000000" />
</requestFiltering>
</security>
</system.webServer>
来源:http://stackoverflow.com/questions/20942636/webapi-cannot-parse-multipart-form-data-post
答案 1 :(得分:1)
在分析日志时,我意识到我们的客户端代码对产生的线程数量极为激进。我给客户端添加了一个限制器来减慢速度,服务器更喜欢这个。我相信我的原始服务器代码更改几乎没有影响。输出的差异可能是由于网络延迟等其他变量造成的。
以下是我更新客户端代码的方法:
// New private field in my client class
private SemaphoreSlim _semaphore = new SemaphoreSlim(Settings.Default.MaxConcurrentRequests, Settings.Default.MaxConcurrentRequests);
// Added continuation to Polly tasks
foreach (var file in files)
{
var task = retryPolicy.ExecuteAsync(() => SendIllustrationRequest(file));
task.ContinueWith((x) => _semaphore.Release());
_transactionTasks.Add(task);
}
Task.WaitAll(_transactionTasks.ToArray());
// Added semaphore wait method to the start of the SendIllustrationRequest(file) method
_semaphore.Wait();
这项技术对我来说效果很好,因为我使用Polly和Polly开始立即执行任务。如果您可以选择在不立即启动任务的情况下创建任务,则还有其他选项。
答案 2 :(得分:0)
让我说明这不是你的代码失败的答案,但我有一个问题,更容易被问到一些空间来显示一些格式化的代码。如果没有提供任何帮助/指示,我会/可以将其删除作为答案。
我假设您的方法是从传入的Web api请求中调用的。是不是有任何理由让你一直没有这样做#34;&#34;?这是构造它的最简单的方法,并且由于代码不良/有缺陷而导致死锁的可能性最小(如果您刚开始使用Tasks和async / await ,那么这很容易做到)。 / p>
// new signature (I changed the name and prefixed Async which is a common convention)
internal static async Task<string> ExtractFileAsync(HttpRequestMessage request)
{
if (request.Content.IsMimeMultipartContent())
{
string uploadRoot = ServiceHelper.GetUploadDirectoryPath();
var provider = new MultipartFormDataStreamProvider(uploadRoot);
await request.Content.ReadAsMultipartAsync(provider);
/* rest of code */
}
}
// calling method example from some Web API Controller
public async Task<IHttpActionResult> Post(CancellationToken token)
{
var result = await ExtractFileAsync(Request).ConfigureAwait(true);
/*some other code*/
}