Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected end of stream at Microsoft.AspNetCore.WebUtilities.MultipartReaderStream

Tags:

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

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

Controller

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;
    }
}
like image 809
Shaun Luttin Avatar asked Apr 06 '16 07:04

Shaun Luttin


2 Answers

I got almost the same exception recently. I'm saying almost because they actually renamed the exception to Unexpected end of Stream, the content may have already been read by another component., which actually means that something already consumed the body stream. The comments of the following change gives us the understanding of what's happening:

Tratcher commented on Mar 23

...The MVC model binder reads the form and buffers the multipart segments for you, so there's no point in re-parsing request body with the MultipartReader...

So, the question is how to disable the default form binding (reading the request form)?

I found the DisableFormValueModelBindingAttribute attribute in this Mvc.FileUpload sample which disables the form binding and this is what it looks like:

[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)
    {
    }
}

If you want some more information, you can check out the following:

  • Create a filter/sample that shows how to remove the form value providers (rynowak opened this issue on Apr 26)
  • Sample: Issues with Antiforgery + Form + File Uploads (rynowak opened this issue on Apr 26)

Just for info - as commented before, the MVC model binder reads the form, but where can one find the results. The results can be found in the HttpRequest.Form, which has Files.

like image 178
Shoxy Avatar answered Nov 30 '22 18:11

Shoxy


Don't know if this might help you but I came across simular issue "Unexpected end of Stream, the content may have already been read by another component".

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

Code above were added in Startup.cs Configure method.

Hope it helps

like image 32
Dynamic_Pace Avatar answered Nov 30 '22 17:11

Dynamic_Pace