C# Request.Content.ReadAsMultipartAsync 从不返回

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15201255/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 14:26:54  来源:igfitidea点击:

Request.Content.ReadAsMultipartAsync never returns

c#asp.net-web-apic#-5.0

提问by Jon Cahill

I have an API for a system written using the ASP.NET Web Api and am trying to extend it to allow images to be uploaded. I have done some googling and found how the recommended way to accept files using MultpartMemoryStreamProvider and some async methods but my await on the ReadAsMultipartAsync never returns.

我有一个使用 ASP.NET Web Api 编写的系统的 API,我正在尝试扩展它以允许上传图像。我已经做了一些谷歌搜索,发现使用 MultpartMemoryStreamProvider 和一些异步方法接受文件的推荐方法是如何但我对 ReadAsMultipartAsync 的等待永远不会返回。

Here is the code:

这是代码:

[HttpPost]
public async Task<HttpResponseMessage> LowResImage(int id)
{
    if (!Request.Content.IsMimeMultipartContent())
    {
        throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
    }

    var provider = new MultipartMemoryStreamProvider();

    try
    {
        await Request.Content.ReadAsMultipartAsync(provider);

        foreach (var item in provider.Contents)
        {
            if (item.Headers.ContentDisposition.FileName != null)
            {

            }
        }

        return Request.CreateResponse(HttpStatusCode.OK);
    }
    catch (System.Exception e)
    {
        return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
    }
}

I can step through all the way to:

我可以逐步完成:

await Request.Content.ReadAsMultipartAsync(provider);

at which point it will never complete.

在这一点上它永远不会完成。

What is the reason why my await never returns?

我的 await 永远不会返回的原因是什么?

Update

更新

I am attempting to POST to this action using curl, the command is as follows:

我正在尝试使用 curl POST 到此操作,命令如下:

C:\cURL>curl -i -F filedata=@C:\LowResExample.jpg http://localhost:8000/Api/Photos/89/LowResImage

I have also tried using the following html to POST to the action as well and the same thing happens:

我也尝试过使用以下 html 来 POST 到操作,并且发生了同样的事情:

<form method="POST" action="http://localhost:8000/Api/Photos/89/LowResImage" enctype="multipart/form-data">
    <input type="file" name="fileupload"/>
    <input type="submit" name="submit"/>
</form>

回答by yeejuto

I ran into something similar in .NET 4.0 (no async/await). Using the debugger's Thread stack I could tell that ReadAsMultipartAsync was launching the task onto the same thread, so it would deadlock. I did something like this:

我在 .NET 4.0 中遇到了类似的问题(没有 async/await)。使用调试器的线程堆栈,我可以知道 ReadAsMultipartAsync 正在将任务启动到同一个线程上,因此它会死锁。我做了这样的事情:

IEnumerable<HttpContent> parts = null;
Task.Factory
    .StartNew(() => parts = Request.Content.ReadAsMultipartAsync().Result.Contents,
        CancellationToken.None,
        TaskCreationOptions.LongRunning, // guarantees separate thread
        TaskScheduler.Default)
    .Wait();

The TaskCreationOptions.LongRunning parameter was key for me because without it, the call would continue to launch the task onto the same thread. You could try using something like the following pseudocode to see if it works for you in C# 5.0:

TaskCreationOptions.LongRunning 参数对我来说很关键,因为没有它,调用将继续在同一线程上启动任务。您可以尝试使用类似以下伪代码的内容,看看它在 C# 5.0 中是否适合您:

await TaskEx.Run(async() => await Request.Content.ReadAsMultipartAsync(provider))

回答by Zenuka

With help of another answer on stackoverflowand a blog post about targetFramework, I've found that updating to 4.5 and adding/updating the following in your web.config fixes this issue:

在 stackoverflow 上另一个答案关于 targetFramework 的博客文章的帮助下,我发现更新到 4.5 并在您的 web.config 中添加/更新以下内容可以解决此问题:

<system.web>
    <compilation debug="true" targetFramework="4.5"/>
</system.web>
<appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>

回答by Camille Sévigny

I have a working .Net MVC WebAPi project with the following Post method which seems to work well. It's very similar to what you have already so this should be helpful.

我有一个可用的 .Net MVC WebAPi 项目,使用以下 Post 方法似乎运行良好。它与您已经拥有的非常相似,因此这应该会有所帮助。

    [System.Web.Http.AcceptVerbs("Post")]
    [System.Web.Http.HttpPost]
    public Task<HttpResponseMessage> Post()
    {
        // Check if the request contains multipart/form-data.
        if (!Request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
        }
        string fileSaveLocation = @"c:\SaveYourFile\Here\XXX";
        CustomMultipartFormDataStreamProvider provider = new CustomMultipartFormDataStreamProvider(fileSaveLocation);
        Task<HttpResponseMessage> task = Request.Content.ReadAsMultipartAsync(provider).ContinueWith<HttpResponseMessage>(t =>
            {
                if (t.IsFaulted || t.IsCanceled)
                {
                    Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
                }
                foreach (MultipartFileData file in provider.FileData)
                {
                    //Do Work Here
                }
                return Request.CreateResponse(HttpStatusCode.OK);
            }
        );
        return task;
    }

回答by C-F

I came across the same problem with all modern 4.5.2 framework.

我在所有现代 4.5.2 框架中都遇到了同样的问题。

My API method accepts one or more files uploaded using POST request with multipart content. It worked fine with small files, but with big ones, my method just hanged forever because the ReadAsMultipartAsync()function never completed.

我的 API 方法接受使用带有多部分内容的 POST 请求上传的一个或多个文件。它适用于小文件,但对于大文件,我的方法只是永远挂起,因为该ReadAsMultipartAsync()功能从未完成。

What helped me:using an asynccontroller method and awaitfor the ReadAsMultipartAsync()to complete, instead of getting the task result in a synchronous controller method.

什么帮助我:使用async控制器的方法,并awaitReadAsMultipartAsync()完成,而不只是简单的同步控制器方法任务结果。

So, this did not work:

所以,这不起作用:

[HttpPost]
public IHttpActionResult PostFiles()
{
    return Ok
    (
        Request.Content.ReadAsMultipartAsync().Result

        .Contents
        .Select(content => ProcessSingleContent(content))
    );
}

private string ProcessSingleContent(HttpContent content)
{
    return SomeLogic(content.ReadAsByteArrayAsync().Result);
}

And this worked:

这工作:

[HttpPost]
public async Task<IHttpActionResult> PostFiles()
{
    return Ok
    (
        await Task.WhenAll
        (
            (await Request.Content.ReadAsMultipartAsync())

            .Contents
            .Select(async content => await ProcessSingleContentAsync(content))  
        )
    );
}

private async Task<string> ProcessSingleContentAsync(HttpContent content)
{
    return SomeLogic(await content.ReadAsByteArrayAsync());
}

where SomeLogicis just a synchronous function taking binary content and producing a string (can be any kind of processing).

whereSomeLogic只是一个采用二进制内容并生成字符串的同步函数(可以是任何类型的处理)。

UPDATEAnd finally I've found the explanation in this article: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

更新最后我在这篇文章中找到了解释:https: //msdn.microsoft.com/en-us/magazine/jj991977.aspx

The root cause of this deadlock is due to the way await handles contexts. By default, when an incomplete Task is awaited, the current “context” is captured and used to resume the method when the Task completes. This “context” is the current SynchronizationContext unless it's null, in which case it's the current TaskScheduler. GUI and ASP.NET applications have a SynchronizationContext that permits only one chunk of code to run at a time. When the await completes, it attempts to execute the remainder of the async method within the captured context. But that context already has a thread in it, which is (synchronously) waiting for the async method to complete. They're each waiting for the other, causing a deadlock.

这种死锁的根本原因是 await 处理上下文的方式。默认情况下,当等待一个未完成的任务时,当前的“上下文”被捕获并用于在任务完成时恢复方法。这个“上下文”是当前的 SynchronizationContext 除非它是空的,在这种情况下它是当前的 TaskScheduler。GUI 和 ASP.NET 应用程序有一个 SynchronizationContext,它一次只允许运行一个代码块。当 await 完成时,它会尝试在捕获的上下文中执行异步方法的其余部分。但是该上下文中已经有一个线程,它(同步)等待异步方法完成。他们都在等待对方,导致僵局。

So, basically, the “Async all the way”guideline has a reason behind it, and this is a good example.

所以,基本上,“Async all the way”指南背后是有原因的,这是一个很好的例子。

回答by Mykhailo Kmet

I had the same. My solution

我有同样的。我的解决方案

public List<string> UploadFiles(HttpFileCollection fileCollection)
    {
        var uploadsDirectoryPath = HttpContext.Current.Server.MapPath("~/Uploads");
        if (!Directory.Exists(uploadsDirectoryPath))
            Directory.CreateDirectory(uploadsDirectoryPath);

        var filePaths = new List<string>();

        for (var index = 0; index < fileCollection.Count; index++)
        {
            var path = Path.Combine(uploadsDirectoryPath, Guid.NewGuid().ToString());
            fileCollection[index].SaveAs(path);
            filePaths.Add(path);
        }

        return filePaths;
    }

and invoking

并调用

if (!Request.Content.IsMimeMultipartContent())
{
    throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}

var filePaths = _formsService.UploadFiles(HttpContext.Current.Request.Files);