1

I have a .NET Core 2.2 API with a POST for a large file upload via stream.

I'm getting the exception "Multipart body length limit 16384 exceeded."

The exception appears at the line...

section = await reader.ReadNextSectionAsync();

...at the FileStreamHelper.cs (see below).

I use postman to try the upload. My file has 10 MB. I get this exception when I choose Body/binary. When I choose Body/form-data instead I get the exception "Unexpected end of Stream, the content may have already been read by another component.". But I did not read the file by a different component or before. I guess binary should be right. Right?

At my Startup.cs I define

        services.Configure<FormOptions>(options =>
        {
            options.MemoryBufferThreshold = Int32.MaxValue;
            options.ValueCountLimit = 10; //default 1024
            options.ValueLengthLimit = int.MaxValue; //not recommended value
            options.MultipartBodyLengthLimit = long.MaxValue; //not recommended value
        });

I'm using also at the Startup.cs

            app.UseWhen(context => context.Request.Path.StartsWithSegments("/File"),
            appBuilder =>
            {
                appBuilder.Run(async context =>
                {
                    context.Features.Get<IHttpMaxRequestBodySizeFeature>().MaxRequestBodySize = (long)( 2 * System.Math.Pow( 1024, 3 )); // = 2GB 
                    await Task.CompletedTask;
                });
            });

At Program.cs I use

            .UseKestrel(options =>
            {
                options.Limits.MaxRequestBodySize = (long)(2 * System.Math.Pow(1024, 3)); // = 2 GB
            })

At my Controller I use the Attribute [DisableRequestSizeLimit].

What could be the reason for this exception?

How to solve this problem?

All code: Controller

[Route("api/v1/dataPicker/fileUploadStream")]
public class FileStreamUploadController : ControllerBase
{
    [HttpPost]
    [DisableRequestSizeLimit]
    [DisableFormValueModelBinding]
    public async Task<IActionResult> Upload()
    {
        var streamer = new FileStreamingHelper();
        var paths = "c:\\temp\\";
        var tempName = Guid.NewGuid().ToString("N");
        using
            (
            var stream = System.IO.File.Create($"{paths}tempName")
            )
            await streamer.StreamFile(Request, stream);
        string from = $"{paths}tempName";
        string to = $"{paths}{streamer.FileNames.FirstOrDefault() ?? tempName}";
        System.IO.File.Move(from, to);
        return Ok($"Uploaded File {paths}{streamer.FileNames.FirstOrDefault() ?? tempName}");
    }
}

FileStreamHelper

    public async Task<FormValueProvider> StreamFile(HttpRequest request, Stream targetStream)
    {
        if (!MultipartRequestHelper.IsMultipartContentType(request.ContentType))
        {
            throw new Exception($"Expected a multipart request, but got {request.ContentType}");
        }

        var formAccumulator = new KeyValueAccumulator();

        var boundary = MultipartRequestHelper.GetBoundary(
            MediaTypeHeaderValue.Parse(request.ContentType),
            _defaultFormOptions.MultipartBoundaryLengthLimit);
        var reader = new MultipartReader(boundary, request.Body);

        MultipartSection section;
        try
        {
            section = await reader.ReadNextSectionAsync();
        }
        catch (Exception ex)
        {
            throw;
        }
        while (section != null) ...}

    public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
    {
        var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);
        if (StringSegment.IsNullOrEmpty(boundary))
        {
            throw new InvalidDataException("Missing content-type boundary.");
        }

        if (boundary.Length > lengthLimit)
        {
            throw new InvalidDataException(
                $"Multipart boundary length limit {lengthLimit} exceeded.");
        }

        return boundary.Value;
    }

You need more code to see? Please tell me!

Here the postman Headers... enter image description here

postman Body... enter image description here

Frank Mehlhop
  • 1,480
  • 4
  • 25
  • 48
  • 1
    Possible duplicate of [Increase upload file size in Asp.Net core](https://stackoverflow.com/questions/38698350/increase-upload-file-size-in-asp-net-core) – Rahul Sharma May 28 '19 at 11:14
  • @FelixToo I add the code into the initial question bottom end. – Frank Mehlhop May 28 '19 at 11:27
  • @RahulSharma It's not a duplicate because whats relevant from the linked article for Core 2.2 you find also in my code above. – Frank Mehlhop May 28 '19 at 11:28
  • From the duplicate, it mentions setting both the Kestrel limits (which you've done in code) *and* the IIS limits, which are done in Web.config, which you've not shown. Also, you use the same file name, `"tempName"` for every upload. This may cause different problems as you get more users uploading files. – Heretic Monkey May 28 '19 at 11:35
  • @HereticMonkey My .NET Core Web Api does not have a web.config. And yes the name... Right now I would be happy to get the file streamed. I will give them unique names anywhere. – Frank Mehlhop May 28 '19 at 11:38
  • I think it is about streaming the file from **postman** to the Api, because it needs to be splitted. And I guess postman is **not splitting the file**. And the exception says that the (not splitted) binary is to big for the server or Api. Does somebody know how to define this streaming process in postman? – Frank Mehlhop May 28 '19 at 13:39

2 Answers2

3

Please try to retrieve the boundary this way by parsing the content type first:

 var contentType = MediaTypeHeaderValue.Parse(context.Request.ContentType);
 var boundary = HeaderUtilities.RemoveQuotes(contentType .Boundary);

 var reader = new MultipartReader(boundary.Value, request.Body);

 //Rest of the code

Also, please attach a screenshot of your postman request, especially the headers

Felix Too
  • 11,614
  • 5
  • 24
  • 25
  • The boundary I parsed to string because MultipartReader accept only string as first parameter. But it does not work. The exception is still the same. The screen shot of postman I will add to the initial question above. – Frank Mehlhop May 28 '19 at 12:23
  • Sorry about that, it should be `boundary.Value`. I have edited my answer. How did you parse to string? – Felix Too May 28 '19 at 13:26
  • Same result. I used ToString(), but also .Value makes no difference. – Frank Mehlhop May 28 '19 at 13:34
  • Resolved the issue in a .NET 6 app. Why does this have so many upvotes? – Adrian H. Mar 03 '22 at 14:52
0

I had to configure postman differently.. enter image description here

1) Body / form-data

2) under "KEY" enter the field with your mouse, than drop down and choose "File"

3) below Value click the new Button and choose a file

4) write into the field "KEY" the word "File"

That was not easy to find out. ;-) All the Header data wasn't important. Now it works.

Frank Mehlhop
  • 1,480
  • 4
  • 25
  • 48