不使用附件形式来发送二进制数

日期: 2008-04-29 作者:Nicholas GallardoRussell Butek 来源:TechTarget中国

  带附件的SOAP规范定义了怎样随SOAP消息发送二进制附件。但是某些时候您可能不想使用附件来发送二进制数据。例如,微软的 .Net Web 服务引擎不支持带附件的 SOAP 消息(Sw/A),所以如果您想协同使用 .Net,您必须使用附件以外的方法来发送二进制数据。学习一种新的方法来更改现存带附件的 Web 服务来发送二进制数据到另外一个 Web 服务,而且该 Web 服务不支持附件。


  除了使用附件的 SOAP 消息(Sw/A)外,还有很多其他方法来交互二进制数据。我们简单地提到了几个复杂的方法,然后详细地介绍一个最简单的方法。


  DIME 解决方案


  如果您的 Web 服务实现支持 DIME,那么您可以编写一个封装器 WSDL,它的绑定可以处理 DIME 协议,而不是 Sw/A 协议。不过,使用这个方法有两个比较大的问题:


  在 W3C 的 DIME 注释在 2002 年底终止了,所以现在已没有任何关于 DIME 的官方规范了。而没有一个官方规范,很可能除了微软以外,其他人都不在支持这个协议。


  现在没有一个 DIME 的 WSDL 官方绑定。而没有一个官方绑定,很可能一个给定的绑定不会被所有提供商所支持,而且这样的话,DIME 绑定将不能协同操作。


  MTOM 解决方案


  SOAP 消息传输优化机制(SOAP Message Transmission Optimization Mechanism,MTOM)是另外一种二进制数据附件协议,而且它很有希望被所有的 Web 服务提供商所支持。但是到编写这篇文章为止,MTOM 规范还没有制订完成(参看 参考资料),所以在有协同操作能力的 MTOM 实现开始在市场出现时,可能需要一年,甚至两年。


  非附件解决方案


  交互二进制数据最简单的方法就是在 XML 中使用字节流,而且作为 xsd:hexBinary 类型或 xsd:base64Binary 类型。简单说,就是您把附件信息转换为非附件形式的字节数据。使用附件的方式是把附件的原始数据放在消息的附件部分,而且与 SOAP 消息相分离,与之不同的是,使用非附件的方式就是把附件的原始数据直接放在 SOAP 消息中。使用这种方法的缺点就是,因为原始数据放在了 SOAP 消息中,为此 XML 解析器 必须扫描这些数据,即使它从来不会被使用到(例如,在一个 SOAP 处理中间体,类似于一个路由器)。所以它不是一个非常有效的解决方案,但它确实是一个最简单的方法。


  一个详细的示例


  假设您有一个遗留的 Web 服务,它有如 清单 1所示的接口。


  清单 1. 附件的遗留 API。


  package com.ibm.developerWorks.attachment;
  public interface ImageBag extends java.rmi.Remote {
      public void sendImage(java.awt.Image i);
      public java.awt.Image getImage();
  }


  这个接口包含 java.awt.Image s。假设它们是 jpeg 图像。根据 JAX-RPC 协议规范,这些图像将映射到 image/jpeg 类型的附件。但是您不想使用附件,所以您下一步要做的就是创建一个封装应用程序,通过它来委托调用原始的应用程序。这个封装器程序的 API(参看 清单 2)包含了二进制数组,而不是图像。这个封装器程序的 WSDL 包含了 xsd:hexBinary 数据类型。(您可以从这个企业应用程序的 EAR 文件中同时找到原始 Web 服务和封装器 Web 服务的 WSDL 文件,对于这个 EAR 文件,您可以点击这篇文章的顶部或底部的 源代码图标来下载一个 ZIP 文件,然后从这个 Zip 文件中摘取出 EAR 文件。 )


  清单 2. 封装器 API


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


  到现在为止,我们感觉很好。但现在您要理解更困难的部分:封装器的实际实现。在委托方法调用到真正的服务之前,封装器实现必须在 byte[] 和 java.awt.Image 之间转换。请参看 清单 3 来获得封装器 API 的完整实现。这是一个可能的 J2SE 1.4 解决方案。这个转换相对有点复杂,而且这篇文章的主要目的也不是来详细介绍怎样在 byte[] 和 java.awt.Image 之间进行转换,我们把这断代码放在这里仅仅是作为您的参考信息(FYI)。


  清单 3. 封装器 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;
          }
      }
  }


  关于使用 xsd:hexBinary、CDATA、以及 .NET 时的注意事项


  当您不想让 XML 解析器来解析某些二进制数据时,经常可以使用 CDATA 来封装这些二进制数据,所以有些系统会使用 CDATA 来封装 xsd:hexBinary 数据。而 .NET 不能很好地处理 CDATA 数据块。而 5.1.1 版本之前的 WebSphere Web 服务,却经常会把大的 hexBinary 数据(大于 2K)封装到一个 CDATA 数据块中, 所以,即使使用这里提供的方法,那些早期的 WebSphere Web 服务也不能像这篇文章所描述的技巧那样跟 .Net 进行协同工作,除非您要传输的图像非常的小,即小于 2K。
 
  客户端的情况非常类似于服务器端的情况。您想在 byte[] 和 java.awt.Image 之间进行转换。所有客户端类似于服务器端的代码。 (请查看随这篇文章带的 Zip 文件(单击文章顶部或底部的 源代码图标)来获得实际的客户端代码。)


  其他 JAX-RPC 支持的所有附件类型都会映射到相应的 Java 类型,而且您可以把它转换为 byte[] 数据(通常是一个字节流),或者转换回来。所以每一个 Java 类型的实现都将不同于这个实例,不过基本思想都相同。


  一个客户端样例


  既然把一个二进制附件转换为一个嵌入的二进制数据的主要原因是,.Net 不支持带附件的 SOAP 消息(Sw/A),为此我们为这个服务选择给您一个 C# 客户端。在这种情况下,我们基于 .NET Framework SDK version 1.1 构造一个客户端。


  正如上面提到的服务实现,我们转换 java.awt.Image 对象为一个 byte[] 数组。在这种情况下,我们相反把 byte[] 转换为 System.Drawing.Image 对象。正如在 Java 中的情况,我们有许多方法来进行转换,这仅仅是一个样例。


  在 清单 4 中 C# 客户端代码从文件系统装载一个图像,然后通过 sendImage(byte[] i) 操作把它发送给服务器。一旦发送出去,客户端使用 getImage() 操作获得相同的图像,并且使用不同的名称把它保存到文件系统。


  清单 4. C# 客户端实现


  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:    ” +
              “ “);
            }
            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);
          }
      }
  }
     
  注释:在 清单 4 中的 ImageBagWithNoAttachmentsService 对象代表了 .Net 客户端代理。这个对象是使用 wsdl.exe 工具创建的,wsdl.exe 包含在 .Net ramework SDK 1.1 中。下面的 参考资料部分包含了更多关于怎样生成这个代理的信息。


  总结


  除了使用带附件的 SOAP(Sw/A)以外,您还有许多方法可以用来发送二进制数据。在这篇文章中,我们介绍了最简单的一种方法技巧:使用 xsd:hexBinary 来插入二进制数据到 SOAP 消息中。我们使用 xsd:hexBinary 封装了一个服务,在这个服务中我们把 java.awt.Image 转换为了字节数据放到 SOAP 消息中。


  作者简介


  Nicholas Gallardo 是 IBM 公司的软件工程师,现在参与开发 IBM WebSphere Web 服务引擎,而且主要针对引擎的实用性和协同性。Nicholas 在 Austin 和 Texas 开始开发了两个不同的技术后于 2001 年加入 IBM。加入 IBM 时的工作主要包括 WebSphere JNDI 实现,而且开始也在 Tivoli 部门工作过。
 
  Russell Butek 是 IBM Web 服务咨询师,而且也是 IBM WebSphere Web 服务引擎的一个开发人员。他也是 JAX-RPC Java Specification Request (JSR) 专家组的 IBM 代表。他参与了 Apache 的 AXIS SOAP 引擎的实现,并推动了 AXIS 1.0 来遵守 JAX-RPC 1.0 规范。之前,他也是 IBM CORBA ORB 的一个开发人员,而且作为多个 OMG 工作组的 IBM 代表,包括可移植拦截器任务组(而且是该组的组长)、OMG 核心工作组、以及协同操作工作组。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐

  • 云存储:数据的用户体验

    我在前一篇文章中讨论了便携便宜并且拥有网络连接的设备怎么样颠覆我们对设备和应用的陈旧假设。现在我将通过本文来更深入的说明:在云端存储用户数据的能力。

  • 用企业mashup模式作为API引擎

    在最近出版的“mashup模式:现代企业设计和实例”(Addison-Wesley, 2009)一书中,作者Michael Ogrinz表示,PC终端年代的“屏幕抓取”给所有从用户界面抽取原始……

  • 张亚勤:未来计算在“云+端”

    IT界正在发生很大的变化,我将其总结为三个中心、五个基本点。三个中心的第一个中心是以数据为中心,现在不管对于大企业还是小企业来说,数据都很重要……

  • 什么样的企业IT架构是理想的?

    什么样的企业IT架构是理想的?在很多新技术不断涌现的今天,企业用户似乎有了越来越多的选择,但是更多的选择在有些时候也可能会意味着无所适从。