Sie sind auf Seite 1von 8

Web services programming tips and

tricks: Send binary data without using


attachments
Nicholas Gallardo (nlgallar@us.ibm.com), Staff Software Engineer, IBM
Russell Butek (butek@us.ibm.com), Software Engineer, IBM

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.

Tag this!
Update My dW interests (Log in | What's this?) Skip to help for Update My dW interests

Date: 28 Sep 2004


Level: Intermediate
Also available in: Japanese

Activity: 13635 views


Comments: 0 (Add comments)

Average rating (based on 14 votes)

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.
Back to top

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.

Back to top

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.

Back to top

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.

Back to top

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/ImageBagWithNoAttachment
s";

// 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.

Back to top

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.

Back to top

Download

Description Name Size Download method


Source code for the example in this tip ws-tip-noattachcode.zip 28 KBHTTP

Information about download methods

Resources

• Download the client code and the EAR file associated with this article by clicking
on the code icon at the top or bottom of this tip. The EAR file contains both the
attachment service, and the byte[] wrapper service.

• Learn more about Passing files to a Web service in this tip (developerWorks,
February 2004).

• Explore using SOAP attachments with JAX-RPC in this tip (developerWorks,


February 2004).

• Read W3C's SOAP with Attachments specification.

• Check out the W3C's latest draft of the MTOM specification.

• Go to Java API for XML-Based RPC (JAX-RPC) Downloads & Specifications,


which provides links to the JAX-RPC 1.1 specification.
• Download the .NET Framework SDK 1.1. This requires installation of the .NET
Framework Redistributable package as a prerequisite.

• Learn how to create a .NET Web services client proxy with this article from
MSDN.

• Access Web services knowledge, tools, and skills with Speed-start Web services,
which offers the latest Java-based software development tools and middleware
from IBM (trial editions), plus online tutorials and articles, and an online
technical forum.

• Browse for books on these and other technical topics.

• Want more? The developerWorks SOA and Web services zone hosts hundreds of
informative articles and introductory, intermediate, and advanced tutorials on how
to develop Web services applications.

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.