locked
Receiving a MIME attachment from a webservice. RRS feed

  • 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 string

    http://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.1
    Thursday, September 4, 2008 10:10 PM
  • User227810533 posted

    Thanks this saved me :)

    Thursday, June 7, 2018 5:53 AM