Microsoft.AspNetCore.WebUtilities.MultipartReaderStream中意外的流结束

时间:2016-04-06 07:38:02

标签: asp.net-core

System.IO.IOException: Unexpected end of stream.
at Microsoft.AspNetCore.WebUtilities.MultipartReaderStream.<ReadAsync>d__32.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.WebUtilities.StreamHelperExtensions.<DrainAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.WebUtilities.MultipartReader.<ReadNextSectionAsync>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at AspNetCoreFileUpload.Controllers.FileUploadController.<Index>d__0.MoveNext() 
in C:\\GitHub\\StackOverflow\\LargeFileUploadController\\FileUploadController.cs:line 29

Repro:https://github.com/bigfont/StackOverflow/tree/master/LargeFileUploadController

表格

<form action = ""/FileUpload"" method=""post"" enctype=""multipart/form-data"">
    <label for=""myfile1"">File</label>
    <input type=""file"" name=""myFile1"" />
    <label for=""myfile2"">File</label>
    <input type=""file"" name=""myFile2"" />
    <input type=""submit"" value=""Send"" />
</form>

控制器

public class FileUploadController : Controller
{
    [HttpPost]
    public async Task<IActionResult> Index()
    {
        var boundary = GetBoundary(Request.ContentType);
        var reader = new MultipartReader(boundary, Request.Body);

        try
        {
            var section = await reader.ReadNextSectionAsync();
        }
        catch (System.Exception ex)
        {
            return new OkObjectResult(new { ex = ex.ToString() });
        }

        return new OkObjectResult(new { message = "Done" });
    }

    private static string GetBoundary(string contentType)
    {
        var elements = contentType.Split(' ');
        var element = elements.Where(entry => entry.StartsWith("boundary=")).First();
        var boundary = element.Substring("boundary=".Length);
        // Remove quotes
        if (boundary.Length >= 2 && 
            boundary[0] == '"' && boundary[boundary.Length - 1] == '"')
        {
            boundary = boundary.Substring(1, boundary.Length - 2);
        }
        return boundary;
    }
}

3 个答案:

答案 0 :(得分:8)

我最近几乎了同样的异常。我说几乎是因为他们实际上将异常重命名为Unexpected end of Stream, the content may have already been read by another component.,这实际上意味着已经消耗了正文流的东西。以下更改的评论使我们了解正在发生的事情:

  

Tratcher commented on Mar 23

     
    

... MVC模型绑定器读取表单并缓冲多部分段     对你来说,重新解析请求体是没有意义的     MultipartReader ...

  

那么,问题是如何禁用默认表单绑定(读取请求表单)?

我在此Mvc.FileUpload sample中找到了DisableFormValueModelBindingAttribute属性,该属性禁用了表单绑定,这就是它的样子:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        var formValueProviderFactory = context.ValueProviderFactories
                .OfType<FormValueProviderFactory>()
                .FirstOrDefault();
        if (formValueProviderFactory != null)
        {
            context.ValueProviderFactories.Remove(formValueProviderFactory);
        }

        var jqueryFormValueProviderFactory = context.ValueProviderFactories
            .OfType<JQueryFormValueProviderFactory>()
            .FirstOrDefault();
        if (jqueryFormValueProviderFactory != null)
        {
            context.ValueProviderFactories.Remove(jqueryFormValueProviderFactory);
        }
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}

如果您想了解更多信息,可以查看以下内容:

仅供参考 - 如前所述, MVC模型绑定器读取表单,但在哪里可以找到结果。结果可以在HttpRequest.Form中找到,其中包含Files

答案 1 :(得分:8)

不知道这是否对您有所帮助,但我遇到了类似的问题“Stream的意外结束,内容可能已被另一个组件读取”。

app.Use(async (context, next) => {
            context.Request.EnableRewind();
            await next();
        });

以上代码已在Startup.cs配置方法中添加。

希望有所帮助

答案 2 :(得分:0)

我创建了一个MemoryStream,从那里的身体复制了流,并且它的工作就像一个魅力:)关键是您不能两次读取Stream。但是,MemoryStream并非如此。当然,您必须确定缩放比例,我认为这不适用于上传的大型文件。我没有测试。 我改写了Microsoft网站上的示例:enter link description here 这是其中的一部分:

    while (section != null)
{
    ContentDispositionHeaderValue contentDisposition;
    var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);

    if (hasContentDispositionHeader)
    {
        if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
        {
            var ms = new MemoryStream();
            var fileSection = section.AsFileSection();
            await fileSection.FileStream.CopyToAsync(ms);
            ms.Position = 0;
            documentUpload.Attachments.Add(new SimpleFileInstance { FileName = fileSection.FileName, FileStream = ms });

        }
        else if (MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition))
        {
            // Content-Disposition: form-data; name="key"//
            // value
            // Do not limit the key name length here because the 
            // multipart headers length limit is already in effect.
            var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name).Value;
            var encoding = GetEncoding(section);
            using (var streamReader = new StreamReader(
                section.Body,
                encoding,
                detectEncodingFromByteOrderMarks: true,
                bufferSize: 1024,
                leaveOpen: true))
            {
                // The value length limit is enforced by MultipartBodyLengthLimit
                var value = await streamReader.ReadToEndAsync();

                if (string.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase))
                {
                    value = string.Empty;
                }

                formAccumulator.Append(key, value);

                if (formAccumulator.ValueCount > DefaultFormOptions.ValueCountLimit)
                {
                    throw new InvalidDataException($"Form key count limit {DefaultFormOptions.ValueCountLimit} exceeded.");
                }
            }
        }
    }
    section = await reader.ReadNextSectionAsync();
}

documentUpload 是我们的DTO,可以进一步处理文件。在我们的案例中,一些文档被上传到SharePoint。