Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transferring byte array from soap service to android

Tags:

android

ksoap2

I have an android client that makes a request to a soap service. The soap service reads an image into a series of bytes and returns a byte array (quite a large one). Would this be seen as a primitive transfer type? The reason I ask is because I have the service code that reads an image and prints the first 5 bytes. Then it returns the byte array.

@Override
public byte[] getImage() {
    byte[] imageBytes = null;
    try {
        File imageFile = new File("C:\\images\\car.jpg");
        BufferedImage img = ImageIO.read(imageFile);
        ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
        ImageIO.write(img, "jpg", baos);
        baos.flush();
        imageBytes = baos.toByteArray();
        baos.close();
    } catch (IOException ioe) {
        ioe.printStackTrace();
    }
    System.out.println("Got request");
    System.out.println("****** FIRST 5 BYTES: ");
    for(int i=0; i<5; i++) {
        System.out.println("****** " + imageBytes[i]);
    }
    return imageBytes;
}

The output from the service on the server is

****** FIRST 5 BYTES: 
****** -119
****** 80
****** 78
****** 71
****** 13

When I receive this on my android emulator, I print the first 5 bytes and they are completely different to those printed on the server. Here is my android code:

        androidHttpTransport.call(SOAP_ACTION, envelope);
        SoapPrimitive  resultsRequestSOAP = (SoapPrimitive) envelope.getResponse();
        String result = resultsRequestSOAP.toString();
        System.out.println("****** RESULT: " + result);
        byte[] b = result.getBytes();
        System.out.println("****** FIRST 5 BYTES: ");
        for(int i=0; i<5; i++) {
            System.out.println("****** " + b[i]);
        }

And the output is

****** FIRST 5 BYTES: 
****** 105
****** 86
****** 66
****** 79
****** 82

It works fine if I test it with a simple service client written in java. Any ideas why this might be happening?

like image 617
Joeblackdev Avatar asked Dec 05 '22 21:12

Joeblackdev


2 Answers

@Michael, that worked a charm. Here is my final working code that will send a jpeg from a soap service to an android emulator and display it. I'm using jax-ws. Here is the service implementation bean for the operation getImage(), which returns a base64 encoded string of the image bytes.

@Override
public String getImage() {
    byte[] imageBytes = null;
    try {
        File imageFile = new File("C:\\images\\fiesta.jpg");
        BufferedImage img = ImageIO.read(imageFile);
        ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
        ImageIO.write(img, "jpg", baos);
        baos.flush();
        imageBytes = baos.toByteArray();
        baos.close();
    } catch (IOException ioe) {
        ioe.printStackTrace();
    }
    return (imageBytes != null) ? Base64.encodeBase64String(imageBytes) : "";
}

Now, here is the android code that will invoke the service and get the encoded image content, decode it to a byte array, create a bitmap and display it in the emulator:

public class ImageSoapActivity extends Activity {

private static final String NAMESPACE = "http://image.webservice";
private static final String URL = "http://10.0.2.2:8080/images?wsdl";
private static final String METHOD_NAME = "getImage";
private static final String SOAP_ACTION = "http://image.webservice/getImage";   

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); 
    envelope.setOutputSoapObject(request);
    HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
    try {
        androidHttpTransport.call(SOAP_ACTION, envelope);
        SoapPrimitive  resultsRequestSOAP = (SoapPrimitive) envelope.getResponse();
        String result = resultsRequestSOAP.toString();
        if (result != "") {
            byte[] bloc = Base64.decode(result, Base64.DEFAULT);         
            Bitmap bmp = BitmapFactory.decodeByteArray(bloc,0,bloc.length);
            ImageView image = new ImageView(this);
            image.setImageBitmap(bmp);
            setContentView(image);
        }
    } catch (Exception e) {
        System.out.println("******* THERE WAS AN ERROR ACCESSING THE WEB SERVICE");
        e.printStackTrace();
    }        
    }
}

enter image description here

I hope this is useful for someone else who may need it. You must also set the permission: <uses-permission android:name="android.permission.INTERNET"></uses-permission>. Also, note that the emulator is connecting to the machine's localhost on 10.0.2.2, not 127.0.0.1

like image 130
Joeblackdev Avatar answered Dec 26 '22 18:12

Joeblackdev


It appears that you're missing an important decoding step. The code that generates the SOAP response has to be encoding the byte array so that it can be represented as a string in the XML. It's difficult to say how you should be decoding it without knowing the encoding mechanism used on the server side (base64, etc.).

In your code above you are calling String#getBytes(), which encodes the given string as binary data using the default character set (which is UTF-8 on Android). This is not the same as decoding the original image data. UTF-8 could not be used as a general purpose encoding mechanism since it cannot represent any arbitrary sequence of bytes (not all bytes sequences are valid UTF-8).

Check the service code to see how it is encoding the binary data and use an appropriate decoding mechanism on the client side, and it should resolve your issue.

HTH.

EDIT: This faq (for an older version but likely still applies) recommends encoding the byte array as base64 and wrapping it in a SoapPrimitive. Personally I would use Commons Codec and its Base64 class to encode the byte array as a string on the server side and use it on the client side to decode this string back to the original byte array.

One thing to watch out for... Android actually bundles an older version of Commons Codec so that might trip you up. In particular it does not include the encodeBase64String convenience method, so you'll need to experiment or do some research to find out what decoding methods exist on the Android platform. You can use android.util.Base64 on the client side but make sure to use the correct flags that match the encoding style you used on the server side. Good luck.

like image 42
Michael Sims Avatar answered Dec 26 '22 16:12

Michael Sims