Android to WCF: Streaming multi part binary images

Yes, the article everyone has been waiting for with baited breath: how to stream MIME type of content from an Android mobile device to a WCF REST-ful service.

I know I have been getting little twitches of wood just thinking about how awesome this post is going to be. But first:

What the hell is multi-part messaging? (or MIME for the acronym obsessed)

Up until 3 weeks ago, MIME was just a word I had only seen in the garbled mess of an .toString() email header. I am not sure what I thought of ‘MIME’ when I used to see it amongst all the other courier-font’ed crap in an Outlook email window. Usually, I only got to see the email headers as a result of an email thread being completely fucked up by too many replies, and Outlook then doing a spot-on rendition  of NASA Launch Control circa January 1986.

I still am not quite sure what it is. But here is a great link:

http://www.creative-wisdom.com/teaching/network/mime.shtml

Look at that, that link is legit. It really does look like it was built in 1995 with Netscape Communicator 4.1.

But back to MIME, it’s a way of formatting disparate types of data in a manner that allows it to be sent across the wire. It’s really not all that interesting.  If you really want to know, check out the RFC:

http://www.ietf.org/rfc/rfc2045.txt

Who writes these RFCs?

Seriously, 40 pages describing MIME? I think I’d rather perform ocular plastic surgery on myself with a flashlight and an exacto knife before you could get me to be apart of one of these nerd fest RFC groups.

The problem: Sending a picture from Android to WCF

Nobody cares about MIME. Im just going to say it, because we’re all thinking it. Back to the problem, my application takes pictures on the phone and needs to upload it to a central server (which is running WCF). Now there are two ways to go about doing this, one way is defining a method signature on your server such as:

public void uploadImage(byte[] imageBytes);

Now this works fine if you have a small amount of data to send. However, if you are sending a MBytes, GBytes, or even TBytes, this is not a good design choice. Primarily because before the method is executed by WCF, the entire contents of imageBytes will be loaded into memory. I think its not hard to see why that might be a problem in large file cases.

Also, WCF is primarily an XML driven messaging platform. It works great if you are actually sending text, but really, if I am sending a picture, should that be encoded into XML? That doesn’t make a lot of sense.

WCF & MTOM: Don’t buffer it, stream it

WCF implements MTOM (Im not sure what it stands for, and I am sure nobody cares) . It’s a protocol enhancement which allows for large binary data to be streamed across the wire in a sequence of MIME formatted messages. Further, it also allows for the binary data to be transmitted as binary data, and not need to be encoded in XML.

So putting it into code: using MTOM to transmit a picture to a web service:

Method Signature: Your WCF operation contract would now look something like:

public void uploadImage(Stream image)

That’s just a regular ole System.IO.Stream object. Now in your URL template for the WCF operation, you would not include any reference to {image} , since this parameter will not be apart of the method signature.

Instead, when WCF sees this signature, and then figures out the {image} parameter is not part of the URL template, it knows to look in the body of the HTTP request to find the beginning of a MIME sequenced stream of data.

As you read the stream in your code, WCF under the covers is streaming forward the bytes received, as opposed to waiting for it to all download and then overload your memory

But wait, you need to adjust your bindings

It wouldn’t be WCF if you didn’t have to fiddle with XML somewhere. In this case, to enable WCF to stream binary data as mentioned above, you need to modify the binding configuration to enable streaming, and to increase your max message sized received from the default 65000:

<bindings>
      <webHttpBinding>
        <binding maxReceivedMessageSize="10000000" maxBufferSize="10000000" transferMode="Streamed"/>
      </webHttpBinding>
</bindings>

On Android, the code to consume a WCF web service with a Stream parameter:

It’s really simple once you get the right JARs. Android evidently doesn’t ship with the full complement of Apache HTTP libraries in JAVA. So you will need to download the following two and link them in your Eclipse project:

apache-mime4j-0.6.1.jar

httpmime-4.0.1.jar

After that, its all gravy son. Your code will look something like this:

DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(query);
ResponseHandler<String> responseHandler = new BasicResponseHandler();

//This is the new shit to deal with MIME
MultipartEntity entity = new MultipartEntity();
entity.addPart("image", new FileBody(imageFile, "image/jpeg"));
httppost.setEntity(entity);

String responseString = httpclient.execute(httppost, responseHandler);

Boom goes the dynamite.

WTF?! My binary data contains more bytes when I read the Stream in WCF than when I send it from Android

Now here’s the rub. This one snagged me for a couple of days, because the pictures I was receiving in the WCF were larger than what was sent from Android.

Turns out that when WCF reads in this streamed data, it also appends the MIME header to the start of the Stream. I’m not sure why, but you need to strip this header out in order to retain fidelity of the original data.

Naturally there really wasn’t an easy way to do this. But if you look at Wireshark, you will see the MIME header has a standard text pattern.  I hacked together this lovely piece of code to get rid of it:

        internal static byte[] GetBytesFromStream(Stream stream, System.Text.Encoding encoding)
        {

            // Read the stream into a byte array
            byte[] data = ToByteArray(stream);
            List<byte> dataList = new List<byte>();

            // Copy to a string for header parsing
            string content = encoding.GetString(data);

            string matchValue = "Content-Transfer-Encoding: binaryrnrn";
            int lastIndex = content.LastIndexOf(matchValue);

            int startingIndex = lastIndex + matchValue.Length;

            byte[] bytes = encoding.GetBytes(content.Substring(0, startingIndex - 1));
            int bytesLength = bytes.Length;

            for (int i = bytesLength + 1; i < data.Length; i++)
            {
                dataList.Add(data[i]);
            }

            return dataList.ToArray();
        }

And voila, at the end of it, I had my original piece of binary data as sent from the Android device.

Coming next week, what to do when your Android device becomes a closet stoner

About Bobby Gill

I am the creator of Bahndr, founder of New York based app development lab, Blue Label Labs and editor at Idea to Appster. I like crepes and I am fascinated by big data.

  • Kaminari

    Hey Bobby,

    Appreciate your help on the subject. I know its too much to ask. But it would be good if you could upload the Android code you used to send the image as stream and also the code which converts the stream back to image in the WCF. I tried your above code but get stumped when my WCF throws “Parameter Invalid” exception when converting the byte data. Please help

    Thanks.

    • http://www.oscial.com Bobby Gill

      Would be happy to:


      public static SendActionResponse sendAction(String url)
      throws ClientProtocolException, IOException,
      ParserConfigurationException, SAXException {
      HttpPost postMethod = null;
      DefaultHttpClient httpClient = new DefaultHttpClient();

      Log.v(Constants.DEBUG_TAG,
      "CreateIcebreakerTask.sendAction: Contacting server at url:"
      + url);
      postMethod = new HttpPost(url);
      ResponseHandler responseHandler = new BasicResponseHandler();
      String responseBody = null;

      responseBody = httpClient.execute(postMethod, responseHandler);

      SendActionResponse response = null;

      response = SendActionResponse.Deserialize(responseBody);

      return response;

      }

  • http://www.viettel.com.vn kiddemon

    Hey Bobby, can you pls send me a project using multipart, i am an developer for Android/Ios, i working with VS2010, using RESTFUL web service to tranfer an image b/w client & android device, i writing server back-end by C#, so when i send an object return to client, i have create twice an object: onece in provider (this provider work with database) and again is with the service.
    So if when i want to send an json like that: {result:{responese{userID,fullname}, errorCode,SessionID} so if when i want return like that, i must create an object 2 times: one in provider and one in service. So what i must do to create it one time ? Pls give an idea, thanks you very much. If you like, i will send you an project for your interview.
    Thanks & Best regard, KID.

  • http://none thanksman

    dude, thank you for the article. well thought out and explained.

  • http://twitter.com/mnhmilu mnhmilu

    Thanks for your wonderful article.Please correct the line in

    internal static byte[] GetBytesFromStream(Stream stream, System.Text.Encoding encoding)
    {

    ………………………
    string matchValue = “Content-Transfer-Encoding: binaryrnrn”; to
    string matchValue = “Content-Transfer-Encoding: binaryrnrn”;

    I have lost few hair of my head to figure out the problem,other wise uploaded image will be corrupted! hope it will helpful for others :) Thanks again!