4

I have to send a file to my webservice, but the webservice assumes the file (byte Array) as a base64Binary.

Before the encoding, the byteArrayFile is saved on disk as a regular File. (I'm doing it just for testing)

So, in my Java client for webservice, I'm sending the information this way:

String file = new sun.misc.BASE64Encoder().encode(byteArrayFile);
port.sendFileToWebService(file);

The webservice have to decode the information and save the received file on disk.

    [WebMethod]
    public string sendFileToWebService(string file)
    {

        string dirname = HttpContext.Current.Request.PhysicalApplicationPath + "\\Attachments\\";
        if (!System.IO.Directory.Exists(dirname))
        {
            System.IO.Directory.CreateDirectory(dirname);
        }
        string filename = dirname + "/" + "file.sim";
        WebClient myWebClient = new WebClient();
        myWebClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
        byte[] byteArray = null;

        byteArray = Convert.FromBase64String(file.Replace("\n", ""));

        byte[] responseArray = myWebClient.UploadData(filename, "POST", byteArray);
        return "Webservice says OK";
    }

The problem is:

The file saved on disk (before encoding) and the file decoded with C# are not equals. I don't know if it's a problem in Java encoding or C# decoding.

Any suggestions, including changing file types or logic process, will always be appreciated.

Thanks in advance!

EDIT - File comparison:

Original File http://img819.imageshack.us/img819/820/originalu.png

Decoded File (after Java encoding) http://img826.imageshack.us/img826/3184/processed.png

CalypsOOO
  • 73
  • 2
  • 11
  • The original file is saved on my local disk. The other file is sended to Webservice that saves the file on server disk. I'm able to access both locations and open the files with Notepad++ and confirm that both size and content are different in both files. – CalypsOOO Aug 04 '10 at 22:23
  • Why are you using a WebClient in your C# code ? – nos Aug 04 '10 at 22:26
  • I'm not sure... I took this code from a forum. – CalypsOOO Aug 04 '10 at 22:37
  • Try to use normal file i/o to write the byte array to a file, using a webclient here seems very, very dodgy. – nos Aug 05 '10 at 01:30
  • 1
    @CalypsOOO I think the fact that you "took this code from a forum" lies at the heart of your problems. :) Specifically, you don't need base-64 or web clients for this, it will only complicate things. Think about what you want to do (send/recieve binary data to/from a web service) and look up the best practices for doing it. – bzlm Aug 05 '10 at 07:54
  • When you read the base64 encoded text on the client, are you reading it into a buffer? Base64 reads four bytes and writes three. If you are reading from a InputSteam or GZipStream, it might not return a buffer with a length evenly divisible by four. – JoshRoss Nov 03 '10 at 18:13

2 Answers2

3

I know that the XSD standard specifies a data type called base64Binary. What this should allow for, is your [WebMethod] parameter to be a byte[]. Then the underlying service stack will encode the byte array to a base64 string.

For example, I just did a quick Java service like this

   @WebMethod(operationName = "TestByteArray")
    public void testByteArray(byte[] data) {

    }

And the relevant parts of the generated WSDL look like this:

<operation name="TestByteArray">
    <input wsam:Action="jordan.services/EncodingTests/TestByteArrayRequest" message="tns:TestByteArray"/>
    <output wsam:Action="jordan.services/EncodingTests/TestByteArrayResponse" message="tns:TestByteArrayResponse"/>
</operation>

And

<xs:complexType name="TestByteArray">
    <xs:sequence>
        <xs:element name="arg0" type="xs:base64Binary" nillable="true" minOccurs="0"/>
    </xs:sequence>
</xs:complexType>

I have also done a test in .Net:

[WebMethod]
public void testByteArray(byte[] bytes) {
}

Relevant parts of the generated WSDL:

<wsdl:portType name="TestWSSoap">
    <wsdl:operation name="testByteArray">
        <wsdl:input message="tns:testByteArraySoapIn"/>
        <wsdl:output message="tns:testByteArraySoapOut"/>
    </wsdl:operation>
</wsdl:portType>

And

<wsdl:types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
        <s:element name="testByteArray">
            <s:complexType>
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="1" name="bytes" type="s:base64Binary"/>
                </s:sequence>
            </s:complexType>
        </s:element>
        <s:element name="testByteArrayResponse">
            <s:complexType/>
        </s:element>
    </s:schema>
</wsdl:types>
Jordan S. Jones
  • 13,703
  • 5
  • 44
  • 49
  • Thank you. Very helpful comment. My problem is related to encoding and decoding in base64 between Java and C# – CalypsOOO Aug 04 '10 at 23:32
  • 1
    @CalypsOOO - Right, my suggestion is that you don't need to do the base64 encoding before/after the WS call. Just send the raw byte array. When you receive it in c#, just post the byte array (or, write it out directly). If you encode before, then you are sending a xsd:string over the wire instead of xsd:base64Binary as you mentioned in the first sentence of your question. – Jordan S. Jones Aug 05 '10 at 00:04
  • Hum, Ok. Sorry, I hadn't understood your suggestion. Did you already tested it before? I thought base64Binary was a String, because encoding anything to base64Binary returns a String. Similiar question here: http://stackoverflow.com/questions/2551525/failed-sending-bytes-array-to-jax-ws-web-service-on-axis – CalypsOOO Aug 05 '10 at 01:16
  • 2
    @CalypsOOO, Yes I have tested it before. When you pass a byte array through a webservice where the type is base64Binary, the underlying webservice stack will convert the binary array to a base64 string for you. Then on the server, when the request comes it, it will base64 decode it back to a byte array for you. My suggestion is to try it. – Jordan S. Jones Aug 05 '10 at 01:40
  • Thank you very much! You're totally right! If it's defined as a byteArray, we have to send a byteArray. Thank you! – CalypsOOO Aug 16 '10 at 22:46
1

Try use normal file i/o instead of a WebClient

public string sendFileToWebService(string file)
{

    string dirname = HttpContext.Current.Request.PhysicalApplicationPath + "\\Attachments\\";
    if (!System.IO.Directory.Exists(dirname))
    {
        System.IO.Directory.CreateDirectory(dirname);
    }
    string filename = dirname + "/" + "file.sim";
    byte[] byteArray = Convert.FromBase64String(file);
    File.WriteAllBytes(filename, byteArray ); //might wanna catch exceptions that could occur here
    return "Webservice says OK";
}
nos
  • 223,662
  • 58
  • 417
  • 506