Asked by:
Receiving a MIME attachment from a webservice.

Question
-
User-264845182 posted
Hello.
I'm trying to build a client using .NET/C# that is capable of
communicating with an existing Webservice written in Java using Apache
Axis. Now, I've encountered a problem trying to retrieve binary data
(i.e. downloading files) which is sent by the service using the "SOAP
with attachments" format (as per
http://www.w3.org/TR/SOAP-attachments), i.e. as a MIME
multipart/related message. Apparently, the.NET Webservice library does
not seem to understand this form of message, at least I've not found a
way to make it accept one. The WSE doesn't help either, from what I
gather, it only provides support for DIME, which in turn the Web
Service is unable to provide.
The only promising approach I've found so far would be to create a
SoapExtension class (similar to what is described in the article at
http://msdn.microsoft.com/msdnmag/i...ds/default.aspx)
which, when added to the WebMethods that return MIME messages,
intercepts the response stream, parses the MIME content, extracts the
XML body and replaces the original MIME content with it and ... well, I
admit I'm not that far yet, but I suppose I just have to save the
attachments somewhere and find a way to pass them to the client calling
the method.
However, this seems to be a lot of work compared to the relative ease
with which it was possible to interface with other WebService methods
that didn't involve attachments, especially because it would require me
to write my own MIME parser... So if there's an easier way, I'd love to
hear about it. Any suggestions would be welcome. Thanks.Thursday, June 7, 2007 7:19 AM
All replies
-
User-285988996 posted
I assume you are using WSE 2.0 SP3?
If you are on .Net 2.0 try using WSE 3.0. If I'm not mistaken WSE 3.0 is able to handle MIME
Thursday, June 7, 2007 9:15 PM -
User-264845182 posted
Apparantely not - WSE 3.0 supports MTOM and WSE 2.0 supports DIME but the service i am consuming spits out a SOAP message with MIME style attachments (example below...).
Content-Type: multipart/related; boundary="----=_ABC_BOUNDARY_1181125115315.29613158"; type="text/xml"; start="<SOAPEnvelope>"
------=_ABC_BOUNDARY_1181125115315.29613158
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: binary
Content-Id: <SOAPEnvelope><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ABC_xsd="http://www.openmobilealliance.org/schema/ABC/v1_0" xsi:type="soapenv:Envelope"><soapenv:Body>....xml stuff in here....</soapenv:Body></soapenv:Envelope>
------=_ABC_BOUNDARY_1181125115315.29613158
Content-Type: image/jpeg
Content-Transfer-Encoding: binary
Content-Id: <getmedia.aspx.Job0>.......................binary content here............
I'm trying a crude method of reading in an HttpWebResponse ResponseStream and wondering if i can get the first section with the SOAP envelope and then read in the binary of the attachment but so far so unsuccessful - can't seem to read the encoding of the binary correctly so not sure what to do there. A company has posted a framework for this type of problem which supposedly covers the bridging between a SOAP / SwA style webservicing here... http://www.alotsoft.com/alotsoftweb/soap_attachment.jsp Haven't tried to implement it as yet though...
Most posts regarding this seem to point to people getting the web service to use DIME or MTOM instead but I don't have a choice here and must use the output.
Friday, June 8, 2007 5:04 AM -
User910519897 posted
Hello,
Did you get any further with this?
I am also forced to work with a java web service and cannot tell them to change it.
I can't find any info about accessing attachments for an rpc encoded web serivice...
Vida
Monday, December 17, 2007 4:40 AM -
User-264845182 posted
I did, but not in a way i would have liked. To use the WSDL and automatic web service handling would have been nicer but I ended up manually disentangling the output. I used HttpWebRequest to create the SOAP request and then the response i had to iterate through several times. The first times I used a StreamReader to read the SOAP response text and to find the barriers to the attachment. Next, once i had the upper and lower bound of the attachment, i used a BinaryReader to get the attachment. NB. StreamReader couldn't be used to read the binary attachment.
So, the response had to be held in memory as a byte[] object and iterated through several times - a clunky solution i'm not happy with. I would have preferred to use a type of Stream to go through just once but couldn't get that to work!
Jared
Monday, December 17, 2007 5:22 AM -
User-1643080966 posted
Hi, So i am facing the same problem as you posted above. So you say you used a StreamReader to figure out the barriers and then read them using BinaryReader, how do you figure out the barriers to the attachment. In this case my attachment is a PDF. I tried looking at %PDF and %EOF etc and somehow Adobe does not open my document, so looks like i am missing something.
Hari.
Thursday, April 24, 2008 7:15 PM -
User1398576633 posted
Hi, I have the same requirement and if you have already achieved this with alotsoft component, could you please let me have the code samples
Thursday, August 28, 2008 8:45 PM -
User1075529774 posted
Hi jarebarr,Could you provide sample code of how you done it using your method? I am facing same problem also and it is very urgent!
Hope you can reply ASAP
THanks
Thursday, August 28, 2008 9:42 PM -
User-264845182 posted
Hi, first you need to have the response in a byte[] array -this can be done by a simple function sending the ResponseStream through it:
private byte[] GetData(Stream input)
{
MemoryStream ms = new MemoryStream();
int read = 0;
byte[] buffer = new byte[1024];
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
This contains both ascii and binary data so you would need to first put it through a stream reader to first extract the Soap or Message text headers
MemoryStream ms = new MemoryStream(data);
StreamReader sr = new StreamReader(ms);
string txt = sr.ReadToEnd();
This will help read the response as text which will work for the first bit. Studying the text you will be able to work out the dividers and the exact characters of the dividers. The text will help find the string representations of the two boundaries of the binary attachments. Once you know the string representations of the two boundaries, you need the binary versions of them. In my case, the first boundary had two line breaks after it, whereas the 2nd boundary had a line break and hyphens before it...
byte[] boundary1 = System.Text.Encoding.UTF8.GetBytes(strBoundary1 + "\r\n\r\n");
byte[] boundary2 = System.Text.Encoding.UTF8.GetBytes("\r\n------" + strBoundary2);Now that you have the two boundaries in binary, you can do a crude binary search to return the binary data between these two values, which will be the attachment.
private byte[] GetAttachmentData(byte[] data, byte[] boundary1, byte[] boundary2)
{
if (data == null || boundary1 == null || boundary2 == null)
return null;
if (boundary1.LongLength > data.LongLength)
return null;
long i, j, startIndex;
bool match;
int boundary1Pos = 0;
for (i = 0; i < data.LongLength; i++)
{
startIndex = i;
match = true;
for (j = 0; j < boundary1.LongLength; j++)
{
if (data[startIndex] != boundary1[j])
{
match = false;
break;
}
else if (startIndex < data.LongLength)
{
startIndex++;
}
}
if (match)
boundary1Pos = Convert.ToInt32(startIndex - boundary1.LongLength);
}
int pos1 = boundary1Pos + boundary1.Length;
int pos2 = data.Length - boundary2.Length;
int length = pos2 - pos1;
try
{
byte[] output = new byte[length];
Array.Copy(data, pos1, output, 0, length);
return output;
}
catch { }
return null;
}Hope this helps, it never was an ideal solution - but at least it worked for me.
Jared
Friday, August 29, 2008 11:51 AM -
User-1643080966 posted
Yea,
I was back to good old byte stream parsing and examining the raw http response (using some tool) and picking my start and end points. In my case, we only expect PDF files and i was able to get around it by looking for specific characters %PDF and %EOF.
I read my entire http response into a local byteStream and manipulated it for my needs
try
{
while (1 == 1){
if (by1 == 37 && by2 == 80 && by3 == 68 && by4 == 70) //found %PDF{
fileName = "blah.PDF"; FileStream fs = null; fs = File.Create(fileName); bw = new BinaryWriter(fs); while (!(by1 == 37 && by2 == 69 && by3 == 79 && by4 == 70)) //found %EOF{
bw.Write(by1);
by1 = by2;
by2 = by3;
by3 = by4;
by4 = br.ReadByte();
}
bw.Write(by1);
bw.Write(by2);
bw.Write(by3);
bw.Write(by4);
bw.Close();
retValues.Add(fileName);
i++;
}
by1 = by2;
by2 = by3;
by3 = by4;
by4 = br.ReadByte();
}
}
catch (EndOfStreamException){
}
Friday, August 29, 2008 12:16 PM -
User1075529774 posted
Thanks for your helping..But i still no luck to get binary image from that. .Just start code for get response from Uri ,it consume me more than 15min.I suspected it is because the file is too big.
Below is my code:
byte[] _requestData = this.CreateHttpRequestData(dicParameters);
try{ string uri = webServiceURL + "/" + webMethod;
HttpWebRequest _httpRequest = (HttpWebRequest)HttpWebRequest.Create(uri);
_httpRequest.Method = "POST";
_httpRequest.KeepAlive = false;
//_httpRequest.ContentType = "application/x-www-form-urlencoded";
//_httpRequest.ContentType = "text/xml;charset=\"utf-8\"";
_httpRequest.ContentType = "application/x-www-form-urlencoded;application/xml;text/plain;multipart/related ";
_httpRequest.ContentLength = _requestData.Length;
_httpRequest.Timeout = 30000;
HttpWebResponse _httpResponse = null;
string _response = string.Empty;
_httpResponse = (HttpWebResponse)_httpRequest.GetResponse();
// Waiting a very very long time}
catch(Exception ex)
{
}
Still have any method or third party control or code doing it?
Thanks
Monday, September 1, 2008 5:13 AM -
User-264845182 posted
Hi, the following is what i use to create the web request. If you're getting a long delay, then perhaps its the server not liking the type or request - e.g. method or content type? My request goes to a server on the Lan so i know the response is quick, even with large file attachments. I can see one difference which is line 7 below, I cast a WebRequest to an HttpWebRequest, as opposed to using HttpWebRequest.Create(). I can't remember why i did this but im sure there was a reason! Also see line 14, the SOAP XML is encoded to bytes here.
1 string url = ""; // url of remote server 2 3 HttpWebRequest req; 4 5 HttpWebResponse res; 6 7 req = (HttpWebRequest)WebRequest.Create(url); 8 9 req.Headers.Add("SOAPAction", @"""http://tempuri"""); 10 req.ContentType = @"text/xml;charset=""utf-8"""; 11 req.Accept = "text.xml"; 12 req.Method = "POST"; 13 14 byte[] reqBytes = System.Text.Encoding.UTF8.GetBytes(doc.InnerXml); 15 req.ContentLength = reqBytes.Length; 16 try 17 { 18 Stream reqStream = req.GetRequestStream(); 19 reqStream.Write(reqBytes, 0, reqBytes.Length); 20 reqStream.Close(); 21 22 res = (HttpWebResponse)req.GetResponse(); 23 } 24 catch (System.Net.Sockets.SocketException se) 25 { 26 isError = true; 27 error = se.ToString(); 28 res = null; 29 } 30 catch (WebException we) 31 { 32 isError = true; 33 res = (HttpWebResponse)we.Response; 34 } 35 catch (Exception exc) 36 { 37 throw (exc); 38 //isError = true; 39 //error = exc.ToString(); 40 }
Monday, September 1, 2008 6:23 AM -
User1398576633 posted
I have downloaded the trial version of alotsoft component and using it for large attachments... It is really working fine and easy instead of coding a mime parser from the scratch... Give a try ....www.alotsoft.com
Monday, September 1, 2008 7:58 PM -
User1075529774 posted
Thanks your guys advise and suggestion.I still no luck to get it right.
_httpRequest.GetRequestStream().Write(_requestData, 0, _requestData.Length);
_httpResponse = (HttpWebResponse)_httpRequest.GetResponse();I just write in my web service parameter, and i using watch to track the process,it hits me:
"This stream does not support seek operations. System.SystemException {System.NotSupportedException}"
This is my intranet Web Service.Basically,this Java Web Service(Axis2 Web Service) only return a type of stringhttp://10.54.55.188/App/ImplService/WEB-INF/wsdl/ImpService.wsdl
and this is my xml :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
- <soapenv:Body>
- <ns2:FindDocumentsResponse xmlns:ns2="http://webservices.dds/">
- <indexes>
<DOB>12/08/1970</DOB>
- <document xsi:type="documentIndex" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<caseNo />
<department />
<docID>67</docID>
<documentType>Administrative Forms</documentType>
<episode />
<Code>kiu</Code>
<lastVisitDate>30/05/2008</lastVisitDate>
<pCode>CLM</pCode>
<profileID>1</profileID>
</document>
I had try above method using another java web service from online
http://140.128.198.70/axis/TwoSampPropTest.jws?wsdl
which also return type of string,then my code execute sucessfully.
Anyone know how what is going on?
Thursday, September 4, 2008 10:00 PM -
User1075529774 posted
The java Web Service protocol is using MTOM enabled on HTTP SOAP 1.1Thursday, September 4, 2008 10:10 PM -
User227810533 posted
Thanks this saved me :)
Thursday, June 7, 2018 5:53 AM