15

I'm trying to accept a post from a client (iOS app) and my code keeps failing on reading the stream. Says the message is not complete. I've been trying to get this working for hours it seems like something is wrong in my message format or something. All I'm trying to do is read a string but the developer I'm working with who is doing the iOS part only knows how to send multipart/form-data not content-type json.

Here is exact error:

Unexpected end of MIME multipart stream. MIME multipart message is not complete."

It fails here: await Request.Content.ReadAsMultipartAsync(provider);

Headers:

POST http://localhost:8603/api/login HTTP/1.1
Host: localhost:8603
Accept-Encoding: gzip,deflate
Content-Type: multipart/form-data; boundary=------------nx-oauth216807
Content-Length: 364
Accept-Language: en-us
Accept: */*
Connection: keep-alive

Body:

--------------nx-oauth216807
Content-Disposition: form-data; name="token"

CAAH5su8bZC1IBAC3Qk4aztKzisZCd2Muc3no4BqVUycnZAFSKuleRU7V9uZCbc8DZCedYQTIFKwJbVZCANJCs4ZCZA654PgA22Nei9KiIMLsGbZBaNQugouuLNafNqIOTs9wDvD61ZA6WSTd73AVtFp9tQ1PmFGz601apUGHSimYZCjLfGBo40EBQ5z6eSMNiFeSylym1pK4PCvI17fXCmOcRix4cs96EBl8ZA1opGKVuWizOsS0WZCMiVGvT
--------------nx-oauth216807--

Here is the WebAPI code:

    public async Task<HttpResponseMessage> PostFormData()
    {
        // Check if the request contains multipart/form-data.
        if (!Request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
        }
        try
        {
        string root = HttpContext.Current.Server.MapPath("~/App_Data");
        var provider = new MultipartFormDataStreamProvider(root);

        // Read the form data and return an async task.
        await Request.Content.ReadAsMultipartAsync(provider);

        // This illustrates how to get the file names.
        foreach (MultipartFileData file in provider.FileData)
        {
            Trace.WriteLine(file.Headers.ContentDisposition.FileName);
            Trace.WriteLine("Server file path: " + file.LocalFileName);
        }
        return Request.CreateResponse(HttpStatusCode.OK);

        }
        catch (System.Exception e)
        {
            return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
        }
    }
Anil
  • 2,539
  • 6
  • 33
  • 42
Shaw Levin
  • 175
  • 1
  • 1
  • 9
  • There is a known issue with 5.0 and before bits of Web API where an extra line at the end of the multipart form data request would cause a issue for Web API's multipart parser. This bug was fixed post-5.0 though. – Kiran Jan 06 '14 at 03:55
  • Where is the "extra line" in the incoming request? I could try to fix the request. Also, do you mean .NET 5.0? I am using .NET 4.5. – Shaw Levin Jan 06 '14 at 03:59
  • i mean a CRLF after the last boundary..i.e `--------------nx-oauth216807--` – Kiran Jan 06 '14 at 04:02
  • 1
    I'm encountering the same error "Error reading MIME multipart body part." Is there a workaround fix? Or is there still an issue with web api 2.1? – Patrick T Jan 25 '14 at 09:25
  • I was not able to get this working using the built in parser. I had to write my own. – Shaw Levin Jan 26 '14 at 15:17
  • @ShawLevin It would be helpful if you could post your own parser. Being that ASP.NET MVC is open source, I am trying to find/fix their broken code as this error is still happening for me. – Lev Dubinets Jun 16 '14 at 21:02
  • I posted my parser at the bottom. Hope it helps. – Shaw Levin Jun 18 '14 at 01:09
  • Landuber Kassa :: http://stackoverflow.com/questions/13770536/asp-net-web-api-unexpected-end-of-mime-multi-part-stream-when-uploading-from-fl/17290999#comment42617381_17290999 – Angelos Kyriakopoulos Nov 20 '14 at 21:54

6 Answers6

42

My application was experiencing this error periodically too. Upgrading to WEB API 2.1 did nothing and the exception message is completely useless.

I think what was actually happening though is it was choking on large files. Increasing my max request limits in web.config seemed to fix it right up.

<system.web>
    <httpRuntime maxRequestLength="30000" />
</system.web>

<system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="30000" />
      </requestFiltering>
    </security>
</system.webServer>

(This sets the ceiling to 30 megs. Set it to whatever you need. More info here)

cheesemacfly
  • 11,622
  • 11
  • 53
  • 72
Matt J.
  • 1,780
  • 1
  • 14
  • 17
  • This actually solved the issue. The exception message is wrong. Thx! – Tawani Jan 17 '15 at 23:49
  • 4
    The maxRequestLength property in `` should be specified in kilobytes. 30MB would be 30000. [source](https://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.maxrequestlength(v=vs.100).aspx) – Siriquelle Oct 30 '15 at 14:59
  • I know its an old answer but thank you so much. This solved my problem which I've been looking at for two weeks now. – Tom Aalbers Apr 25 '16 at 20:51
  • Also setting the executionTimeout with debug set to false might help. – Shuto Mbofana Dec 30 '16 at 12:37
12

I encountered this error too. The InnerException is Cannot access a disposed object. This means that something is reading your stream before your call to ReadAsMultipartAsync.
Somewhere before this call Request.Content.ReadAsMultipartAsync(provider), you can call Request.Content.LoadIntoBufferAsync().Wait(), which will load this tream into a buffer and allow you to read it more than once.
This is not an optimal solution, but it works.

Lev Dubinets
  • 788
  • 10
  • 32
  • 1
    I was having the problem intermittently with some image files and nothing else. Not sure what the problem was but this worked perfectly - thanks. – Mathew Collins Sep 11 '14 at 10:15
4

I am leaving this here since it took me quite some time trying other workarounds until I bumped onto the following helpful answer and some people having this issue may end up on this post.

A \r\n needs to be appended at the end of request content stream.

Instead of using this line to read the data:

await Request.Content.ReadAsMultipartAsync(provider);

You will need to:

  1. load the request stream to memory

  2. append the \r\n string that is required

  3. create a stream content from the memory content

  4. manually add the request headers to the stream content

  5. Finally use this instead:

    streamContent.ReadAsMultipartAsync(provider); 
    

Check the answer of Landuber Kassa here for the complete code: ASP.NET Web API, unexpected end of MIME multi-part stream when uploading from Flex FileReference

Community
  • 1
  • 1
2

Just a modification for Shaw Levin answer in case anyone wants to use it.

boundary = value.Substring(0, value.IndexOf("\r\n")); will find the first occurance of the CRLF, you should change it to boundary = value.Substring(0, value.LastIndexOf("\r\n")); so it looks for the last occurance. Otherwise if the content includes a CRLF somewhere in the middle you will lose part of the data in the request.

Reza
  • 115
  • 2
  • 7
1

There were similar error posts, for some, the solution worked is: to mention Id="", name="" attribute to file upload html control, thanks to WebAPI upload error. Expected end of MIME multipart stream. MIME multipart message is not complete

But in my case, it did not resolve with above simple tweak :(

Community
  • 1
  • 1
HydPhani
  • 592
  • 6
  • 13
1

I would not recommend this answer - hopefully there is a better way available now.

Someone asked so here is my custom parser which has been working fine:

Boundary comes from here:

        string value;
        using (var reader = new StreamReader(tempStream, Encoding.UTF8))
        {
            value = reader.ReadToEnd();
            // Do something with the value
        }

        boundary = value.Substring(0, value.IndexOf("\r\n"));

And then we parse the content of the request here:

   public Dictionary<string, BodyContent> ParseContent(string content)
    {
        string[] list = content.Split(new string[] { boundary }, StringSplitOptions.RemoveEmptyEntries);
        string name="", val="";
        Dictionary<string, BodyContent> temp = new Dictionary<string, BodyContent>();
        foreach (String s in list)
        {
            if (s == "--" || s == "--\r\n")
            {
                //Do nothing.
            }
            else
            {
                string[] token = s.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
                val = "";
                name = "";
                foreach (string x in token)
                {

                    if(x.StartsWith("Content-Disposition"))
                    {
                        //Name
                        name = x.Substring(x.IndexOf("name=")+5, x.Length - x.IndexOf("name=")-5);
                        name = name.Replace(@"\","");
                        name = name.Replace("\"","");
                    }
                    if (x.StartsWith("--"))
                    {
                        break;
                    }
                    if (!x.StartsWith("--") && !x.StartsWith("Content-Disposition"))
                    {
                        val = x;
                    }

                }
                if (name.Length > 0)
                {
                    BodyContent b = new BodyContent();
                    b.content = name;
                    if (val.Length == 0)
                    {
                        b.value = "";
                    }
                    else
                    {
                        b.value = val;
                    }
                    temp.Add(name, b);
                }
            }

        }
        return temp;        
    }
Shaw Levin
  • 175
  • 1
  • 1
  • 9