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

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

the 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;
    }
}

      

+5


source to share


3 answers


I recently got almost the same exception. I say 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 is already being consumed by the body thread. The comments of the following change give us an idea of ​​what is going on:

Tratcher commented on Mar 23

... The MVC model binding mechanism reads the form and buffers the multipart segments for you, so it doesn't make sense to re-parse the request body with the MultipartReader ...

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

I found an attribute DisableFormValueModelBindingAttribute

in this example Mvc.FileUpload that disables form binding and it looks like this:



[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 need more information, you can check the following:

Just for information - as commented earlier, the MVC model binder reads the form, but where can the results be found. The results can be found in HttpRequest.Form

which has Files

.

+8


source


I don't know if this can help you, but I ran into the problem "Unexpected end of stream, content may have already been read by another component."

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

      



The code above has been added to the Startup.cs Configure Method.

Hope it helps

+8


source


I created a MemoryStream, copied the stream from the body there, and it worked like a miracle :) The bottom line is that you cannot read Stream twice. However, this is not the case with MemoryStream. Of course you have to be sure to scale, I don't think this will work for really large uploaded files. I have not tested this. I rewrote the example from Microsoft site: enter link description here Here is some of it:

    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 is our DTO for further work with files. In our case, some documents are uploaded to SharePoint.

0


source







All Articles