Skip to main content

Web services programming tips and tricks: Send binary data without using attachments

Nicholas Gallardo (nlgallar@us.ibm.com), Staff Software Engineer, IBM
Nicholas Gallardo works as a software engineer on the IBM WebSphere Web Services engine, where he is focused on serviceability and interoperability. Nicholas came to IBM in 2001 after contributing development efforts to two different technology start-ups in Austin, Texas.
Russell Butek (butek@us.ibm.com), Software Engineer, IBM
Russell Butek is an IBM Web services consultant and is one of the developers of the IBM WebSphere Web services engine. He is also the IBM representative on the JAX-RPC Java Specification Request (JSR) expert group. He was involved in the implementation of Apache's AXIS SOAP engine, driving AXIS 1.0 to comply with JAX-RPC 1.0. Previously, he was a developer of the IBM CORBA ORB and an IBM representative on a number of OMG task forces: the portable interceptor task force (of which he was chair), the core task force, and the interoperability task force.

Summary:  The SOAP with Attachments specification (see Resources) defines how to send binary attachments along with a SOAP message. But there may be cases where you do not want to use attachments to send binary data. For instance, Microsoft's .NET Web services engine does not support Sw/A, so if you want to interoperate with .NET, you must use some other alternative. Learn a new way to modify an existing Web service that uses attachments to send binary data to another service that does not.

Date:  28 Sep 2004
Level:  Intermediate
Activity:  5282 views

There are a number of ways to exchange binary data other than through SOAP with Attachments (Sw/A). We briefly mention a couple of complex methods, but then go into details about the simplest method.

The DIME solution

If your Web service implementation supports DIME, then you could write a wrapper WSDL whose binding deals with the DIME protocol instead of the Sw/A protocol. There are two big concerns with this approach, however:

  • The DIME note in W3C expired at the end of 2002, so there is no official specification for what is called DIME. Without an official specification, it is unlikely anyone other than Microsoft will continue to support this protocol.
  • There is no official WSDL binding for DIME. Without an official WSDL binding, it is unlikely a given binding will be supported by all vendors and, therefore, DIME bindings would not interoperate.

The MTOM solution

SOAP Message Transmission Optimization Mechanism (MTOM) is another binary data attachment protocol that stands a good chance of being supported by all Web services vendors. However, as of this writing, the MTOM specification is not yet completed (see Resources), so it will likely be a year or two before interoperable MTOM implementations begin to show up in the marketplace.


A non-attachment solution

The simplest way to exchange binary data is as a byte array -- in XML, as a xsd:hexBinary or a xsd:base64Binary type. In short, you are turning attachments into non-attachments. Instead of the attachment's raw data appearing in the message in an attachment part separate from the SOAP message part, the raw data appears within the SOAP message itself. The drawback to this solution is that, since the raw data is within the SOAP message, it must be scanned by any XML parser, even if it is never used (for example, in an intermediary, like a router). So it is not a particularly efficient option. It is merely the simplest option.


A detailed example

Assume you have a legacy Web service with the interface in Listing 1.


Listing 1. Legacy API with attachments.
package com.ibm.developerWorks.attachment;

public interface ImageBag extends java.rmi.Remote {
    public void sendImage(java.awt.Image i);
    public java.awt.Image getImage();
}

This interface contains java.awt.Images. Assume they're jpeg images. According to JAX-RPC, these images map to attachments of type image/jpeg. But you don't want to use attachments. So what you do next is create a wrapper application which delegates calls to the original application. This wrapper's API (see Listing 2) contains byte arrays instead of images. This wrapper's WSDL contains xsd:hexBinary data types. (You can find the WSDL files for both the original service and for the wrapper service by peeling apart the EAR file which you can get by clicking the code icon at the top or bottom of this tip and downloading the zip file.)


Listing 2. Wrapper API
package com.ibm.developerWorks.attachment;

public interface ImageBagWithNoAttachments extends java.rmi.Remote {
    public void sendImage(byte[] i);
    public byte[] getImage();
}

So far, so good. But now you come to the hard part: the actual implementation of the wrapper. The wrapper implementation must convert between byte[] and java.awt.Image before delegating the method calls to the real service. See Listing 3 for a complete implementation of the wrapper API. This is one possible J2SE 1.4 solution. It is rather complex, and while it is not the purpose of this tip to explain in detail how to convert between byte[] and java.awt.Image, we present it here for your information.


Listing 3. Implementation of the wrapper API
package com.ibm.developerWorks.attachment;

import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;

import java.awt.image.BufferedImage;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import java.util.Iterator;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;

import javax.imageio.stream.ImageOutputStream;

public class ImageBagWithNoAttachmentsSoapBindingImpl
        extends Component implements ImageBagWithNoAttachments{
    private ImageBag imageBag = new ImageBagSoapBindingImpl();

    public void sendImage(byte[] i) {
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(i);
            ImageIO.setUseCache(false);
            imageBag.sendImage(ImageIO.read(bais));
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    public byte[] getImage() {
        try {
            Image image = imageBag.getImage();
            Iterator iter = ImageIO.getImageWritersByMIMEType("image/jpeg");
            ImageWriter writer = iter.hasNext() ? (ImageWriter) iter.next() : null;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
            writer.setOutput(ios);
            BufferedImage rendImage = null;
            if (image instanceof BufferedImage) {
                rendImage = (BufferedImage) image;
            } else {
                MediaTracker tracker = new MediaTracker(this);
                tracker.addImage(image, 0);
                tracker.waitForAll();
                rendImage = new BufferedImage(image.getWidth(null),
                        image.getHeight(null), 1);
                Graphics g = rendImage.createGraphics();
                g.drawImage(image, 0, 0, null);
            }
            writer.write(new IIOImage(rendImage, null, null));
            writer.dispose();
            return baos.toByteArray();
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

A caution about xsd:hexBinary, CDATA, and .NET

CDATA is often used to wrap binary data that you do not want an XML parser to parse. Some systems wrap xsd:hexBinary data with a CDATA block. .NET doesn't always handle CDATA properly. WebSphere Web services, before version 5.1.1, often wrapped large (greater than 2048 bytes) hexBinary data with a CDATA block, so even with the method presented here, those versions of WebSphere Web services do not interoperate with .NET as described in this tip unless you have very small images.

The client-side situation is similar to the server-side situation. You want to convert between byte[] and java.awt.Image. So the client-side code is similar to the server-side code. (See the zip file that comes with this tip (click on the code icon at the top or bottom of this tip) for some actual client-side code.)

The other JAX-RPC-supported attachment types all map to Java types which you can convert to and from a byte[] (usually through a stream). So while the implementations for each type will be different than this example, the basic idea is the same.


A sample client

Since one primary reason for converting a binary attachment to inlined binary data is that .NET doesn't support Sw/A, we chose to show you a C# client for this service as well. In this case, we build a client based on the .NET Framework SDK version 1.1.

As noted in the service implementation above, we converted a java.awt.Image object into a byte[]. In this case, we go back and forth from byte[] to System.Drawing.Image. Just as in the Java case, there are many ways to perform this conversion; this is just one example.

The C# client code in Listing 4 loads an image from the file system and sends it to the server through the available sendImage(byte[] i) operation. Once sent, the client retreives the same image using the getImage() operation and saves it to the file system with a different name.


Listing 4. Implementation of the C# client
using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Web.Services;

namespace ImageClient
{
    class ImageClient
    {
        [STAThread]
        static void Main(string[] args)
        {
            if (args.Length < 4) {
	            Console.WriteLine("Insufficient argument list: <host> " +
	            	"<port> <sendImageName> <newImageName>");
            }
            else {	
                string host          = args[0];
                string port          = args[1];
                string sendImageName = args[2];
                string newImageName  = args[3];

                ImageBagWithNoAttachmentsService service = 
                	new ImageBagWithNoAttachmentsService();
                service.Url = "http://" + host + ":" + port + 
                	"/AttachmentWar/services/ImageBagWithNoAttachments";

                // Create a cookie container so that the session will be saved 
                // and we can retreive the image.
                service.CookieContainer = new System.Net.CookieContainer();

                ImageClient client = new ImageClient();

                // Turn the image into a byte[] and send it. 
                Console.WriteLine("Sending data to the server.");
                service.sendImage(client.createByteArray(sendImageName));

                Console.WriteLine("");

                // Get the byte[] from the service and turn it into an image.
                Console.WriteLine("Retreiving image data from the server.");
                client.saveAsImage(service.getImage(), newImageName);
            }
        }

		

        public byte[] createByteArray(string imageName) 
        {
            FileInfo fileInfo = new FileInfo(imageName);
            FileStream fileStream = fileInfo.OpenRead();
            byte[] byteArray = new byte[fileInfo.Length];		
            int bytesRead = fileStream.Read(byteArray, 0, byteArray.Length);
            Console.WriteLine("{0} bytes have been read from {1}", 
            	bytesRead.ToString(), imageName);
            return byteArray;
        }


        public void saveAsImage(byte[] bytes, string imageName) 
        {
            MemoryStream memStream = new MemoryStream(bytes);
            System.Drawing.Image image = 
            	System.Drawing.Image.FromStream(memStream);
            image.Save(imageName);
            Console.WriteLine("{0} was created successfully.", imageName);
        }
    }
}

NOTE: The ImageBagWithNoAttachmentsService object in Listing 4 represents the .NET client proxy. This object was created using the wsdl.exe tool that comes with the .NET Framework SDK 1.1. The Resources section below contains more information about how to generate this proxy.


Summary

There are a number of ways that you can send binary data other than through Sw/A. In this tip we detailed the simplest: inlining the binary data using xsd:hexBinary. We wrapped a service using java.awt.Image with a service using xsd:hexBinary.



Download

DescriptionNameSizeDownload method
Source code for the example in this tipws-tip-noattachcode.zip28 KB HTTP

Information about download methods


Resources

About the authors

Nicholas Gallardo works as a software engineer on the IBM WebSphere Web Services engine, where he is focused on serviceability and interoperability. Nicholas came to IBM in 2001 after contributing development efforts to two different technology start-ups in Austin, Texas.

Russell Butek is an IBM Web services consultant and is one of the developers of the IBM WebSphere Web services engine. He is also the IBM representative on the JAX-RPC Java Specification Request (JSR) expert group. He was involved in the implementation of Apache's AXIS SOAP engine, driving AXIS 1.0 to comply with JAX-RPC 1.0. Previously, he was a developer of the IBM CORBA ORB and an IBM representative on a number of OMG task forces: the portable interceptor task force (of which he was chair), the core task force, and the interoperability task force.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and Web services, XML
ArticleID=23515
ArticleTitle=Web services programming tips and tricks: Send binary data without using attachments
publish-date=09282004
author1-email=nlgallar@us.ibm.com
author1-email-cc=
author2-email=butek@us.ibm.com
author2-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers