Reading multipart content from raw http request

27,412

Solution 1

You are adding a StreamContent as a part to the empty MultipartFormDataContent. Naturally, ReadAsMultipartAsync then does nothing. Instead, you want to read the StreamContent. Also, your file contains not just the content, but the headers as well. If you snip off the headers, this ought to work:

using (var stream = File.Open (@"C:\temp\test.txt", FileMode.Open))
{
    // note: StreamContent has no Content-Type set by default
    // set a suitable Content-Type for ReadAsMultipartAsync()
    var content = new StreamContent (stream) ;
    content.Headers.ContentType = 
        System.Net.Http.Headers.MediaTypeHeaderValue.Parse (
            "multipart/related; boundary=cbsms-main-boundary") ;

    // TODO: make this recursive if required...
    var outerMultipart = await stream.ReadAsMultipartAsync () ;

    foreach (var outerPart in outerMultipart.Contents)
    {
        if (outerPart.IsMimeMultipartContent())
        {
            var innerMultipart = await outerPart.ReadAsMultipartAsync () ;

            foreach (var innerPart in innerMultipart.Contents) // do stuff
        }
        else // do other stuff
    }
}

Solution 2

Use HttpContentMultipartExtensions class and ReadAsMultipartAsync method http://msdn.microsoft.com/en-us/library/hh835439%28v=vs.108%29.aspx

Good example is here: http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2

Share:
27,412
user517406
Author by

user517406

Updated on July 11, 2022

Comments

  • user517406
    user517406 almost 2 years

    I am saving a raw HTTP request to a text file and I need to read the multipart content within it. I know there are a number of 3rd party tools to do this but I need to do this with the .NET framework if possible. I have tried reading the txt file into a stream but I don't know how to extract the multipart content. Here is an example of the code I have been using and a sample file I am trying to read from :

        var content = new MultipartFormDataContent();
    
                var stream = new StreamContent(File.Open(@"C:\temp\test.txt", FileMode.Open));
                content.Add(stream);
    
                List<StreamContent> lstStreamContents = new List<StreamContent>();
    
                if (content != null)
                {
                    if (content.IsMimeMultipartContent())
                        await content.ReadAsMultipartAsync(StreamProvider);
    
                    foreach (MultipartContent multiPartContent in content)
                    {
                        foreach (var streamContent in multiPartContent).....
    

    Text file :

    Cache-Control: no-cache
    Connection: Close
    Content-Length: 5216
    Content-Type: multipart/related;
         boundary="cbsms-main-boundary";
         start="<soap-envelope>", text/xml;
         charset=utf-8
    Accept: */*
    Host: hostname
    User-Agent: Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)
    soapaction: ""
    
    This is a multi-part message in MIME format.
    
    --cbsms-main-boundary
    Content-Type: text/xml;
         charset="utf-8"
    Content-ID: <soap-envelope>
    
    <?xml version="1.0" encoding="UTF-8"?>
    <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header>
    <mm7:TransactionID
    xmlns:mm7="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL
    -6-MM7-1-2">5947CCE35D5B4AEFB99DADDDF9472E67</mm7:TransactionID>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
    <mm7:DeliverReq
    xmlns:mm7="http://www.3gpp.org/ftp/Specs/archive/23_series/23.140/schema/REL
    -6-MM7-1-2">
    <mm7:MM7Version>6.5.0</mm7:MM7Version>
    <mm7:MMSRelayServerID>hostname</mm7:MMSRelayServerID>
    <mm7:LinkedID>1-1754394156</mm7:LinkedID>
    <mm7:SenderAddress>
    <mm7:Number>46707630767</mm7:Number>
    </mm7:SenderAddress>
    <mm7:Recipients>
    <mm7:To>
    <mm7:Number>72401</mm7:Number>
    </mm7:To>
    </mm7:Recipients>
    <mm7:TimeStamp>2008-05-08 11:16:39</mm7:TimeStamp> <mm7:Priority>Normal</mm7:Priority>
    <mm7:Subject>Tube test</mm7:Subject>
    </mm7:DeliverReq>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    
    --cbsms-main-boundary
    Content-Type: multipart/mixed;
         boundary="cbsms-sub-boundary"
    Content-ID: <MM7-Media>
    
    --cbsms-sub-boundary
    content-type: application/smil;
        Name=main.smil;Charset=utf-8
    content-transfer-encoding: 7bit
    content-id: <AAAA>
    content-length: 483
    
    <smil><head><layout><root-layout backgroundColor="#FFFFFF"
    background-color="#FFFFFF" height="480px" width="640px"/> <region id="Image" top="0" left="0" height="50%" width="100%" fit="meet"/> <region id="Text" top="50%" left="0" height="50%" width="100%"
    fit="scroll"/>
    </layout>
    </head>
    <body><par dur="4000ms"><img src="smslogo.jpg" region="Image"></img> <text src="smil.txt" region="Text"><param name="foreground-color"
    value="#000000"/>
    </text>
    </par>
    </body>
    </smil>
    
    --cbsms-sub-boundary
    content-type: image/jpeg;
        Name=smslogo.jpg
    content-transfer-encoding: Base64
    content-location: smslogo.jpg
    content-length: 2218
    
    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
    9d86ytQ8z65bYC4MjdzwHeFWzztqxicwsjDyKB4bQjr3IO4b1AsG5VBulAOklldLIS57jVzj
    vKP8r6lZ6ZqBkvDkjkjMYkpXKSRiRuwQSlNiYioomg3Y1drtdU0u4Y0W95BJgKAPbXvE1VwZ
    XYtIPcNfAuCcN4+SSFNs14zBkz2jqcQh2/q6fzzrFvZaTLYNeDdXVG8MGpaytS526q5pbAuo
    OlxA768C2WR2aQlx3nFWWNytoqjF11ZdHsYhG0MbsjaGjsS1K9j06zkuJDRzQRG07XOOwBYV
    mt6xDGIYrl7YxgKUrTu7VXfJd3j89xI6V295JQ1uwba3lk/t5dz1+04rX5vO95YUlG3dH/bi
    78cZM7W5uivvDG07+CSm6+g/NMHLz9QmJuuFdVHGGWQ40G5pGxARZ6B5WoU/25P8iSSoN2c9
    HFMWXLPlaifVy+zTiy5T8rUj6qX2aSSebPbSFjyf06mfVTezT+5cm/1N3qpvZpJK5jtF7lyd
    /U3eqm9mn9y5O/qZ9VN7NJJXM9s3uPKNcNTPqpfZpjZcr9GpH1Uvs0klc120DZ8u+TqP/HJ7
    NO200QVz35MVPHpHJWnVRle8kkg9tq44+UfwJMxk38Bw4klJq5uM2mFOJ97/AIokkkp0+3p+
    OD//2Q==
    
    --cbsms-sub-boundary
    content-type: text/plain;
        Name=smil.txt;Charset=utf-8
    content-transfer-encoding: 7bit
    content-location: smil.txt
    content-length: 9
    
    Tube test
    --cbsms-sub-boundary--
    
    --cbsms-main-boundary--
    

    At the moment I am getting this error 'Unable to cast object of type 'System.Net.Http.StreamContent' to type 'System.Net.Http.MultipartContent'.'

    EDIT :

    foreach (var part in multipart.Contents)
                {
                    if (part.Headers.ContentType.MediaType == "multipart/mixed")
                    {
                        if (part.IsMimeMultipartContent())
                            await part.ReadAsMultipartAsync(StreamProvider);
    
                        foreach (MultipartContent multiPartContent in StreamProvider.Contents)
                        {
    

    This gives the error ''Unexpected end of MIME multipart stream. MIME multipart message is not complete'

  • user517406
    user517406 over 10 years
    Thanks for the answer, I think that is on the right track, however I cannot set Headers.ContentType for the stream object
  • user517406
    user517406 over 10 years
    Found the issue, should be referring to content object rather than stream
  • user517406
    user517406 over 10 years
    Excellent...that gets me 2 content types, "text-xml" and "multipart/mixed"...that enables me to parse the xml, but how would I get the content type "image/jpeg" from within the "multipart/mixed" content?
  • Anton Tykhyy
    Anton Tykhyy over 10 years
    Um... you just get it? By, you know, looping over multipart.Contents?
  • user517406
    user517406 over 10 years
    Looping through multipart.Contents gives me 2 parts, text-xml and multipart-mixed. The question I was asking was how do I get the image from the multipart-mixed part. I am not sure how to iterate through the multipart-mixed part to get at image-jpeg. Please see Edit above which explains this
  • user517406
    user517406 over 10 years
    Figured it out, I needed to set the stream position back to 0
  • Anton Tykhyy
    Anton Tykhyy over 10 years
    I missed that you had nested multipart contents. You iterate through the inner mixed content the same way as through the outer mixed content, except the headers are parsed for you. Your edit is wrong because again you are not reading the inner multi-part content. See my edit here. You must use the return value of ReadAsMultipartAsync!
  • Jesse Chisholm
    Jesse Chisholm almost 6 years
    Oddly enough, new MediaTypeHeaderValue("multipart/related; boundary=cbsms-main-boundary") didn't do exactly the same things as MediaTypeHeaderValue.Parse("multipart/related; boundary=cbsms-main-boundary") Apparently the constructor only wants the subtype, the the other attributes. Whereas Parse knows what to do with the other attributes.