Are You Using AI In Your Job?

We want to understand the real-world applications of AL and ML in business and the impact it will have on all our jobs.

Want to help? Complete the survey, your insights could make a big difference. It will just take one minute.
You'll be the first to get access to the final report.-->

WCF Streaming: Streaming Multi Part Binary Images For Android

Bobby Gill | May 30, 2011

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

Bobby Gill
Co-Founder & Chief Architect at BlueLabel | + posts

Get the latest from the Blue Label Labs’ blog in your inbox

Subscribe

* indicates required