10

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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
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
  • 13,623
  • 29
  • 80
  • 120
  • This project handles multi-part file uploads for use with the bluimp jquery file upload component. https://github.com/i-e-b/jQueryFileUpload.Net check out this file in particular. https://github.com/i-e-b/jQueryFileUpload.Net/blob/master/jQueryFileUpload/FileTransferHandler.ashx.cs – BenCr Jan 07 '14 at 15:40

2 Answers2

11

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
    }
}
Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56
  • Thanks for the answer, I think that is on the right track, however I cannot set Headers.ContentType for the stream object – user517406 Jan 07 '14 at 16:07
  • Found the issue, should be referring to content object rather than stream – user517406 Jan 07 '14 at 16:10
  • 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? – user517406 Jan 07 '14 at 16:39
  • Um... you just get it? By, you know, looping over `multipart.Contents`? – Anton Tykhyy Jan 08 '14 at 07:17
  • 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 Jan 08 '14 at 08:40
  • Figured it out, I needed to set the stream position back to 0 – user517406 Jan 08 '14 at 09:58
  • 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`! – Anton Tykhyy Jan 08 '14 at 21:42
  • 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. – Jesse Chisholm May 31 '18 at 23:22
1

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

Regfor
  • 8,515
  • 1
  • 38
  • 51