用SAAJ1.2发送接收二进制Web服务内容

本文介绍了如何使用SAAJ1.2发送和接收带有二进制内容的Web服务信息,详细展示了创建、发送和接收带有附件的SOAP信息的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在写这篇文章的时候,大多数的Web服务由简单的信息交换组成:客户端连接到Web服务器端,并发送一条消息给服务端。Web服务端处理客户端的请求并发送回应给客户端。这个简单的请求/回应模式,模拟了HTTP协议帮助客户端/服务器端交互的过程。同样由于HTTP,Web服务信息的交换经常必须包含二进制的内容。比如图像,文档或者声音片段。这篇文章介绍了使用SAAJ1.2(SOAP with Attachements API for Java 1.2)来发送和接收二进制内容Web信息。
  
  在深入到传送二进制Web服务内容的复杂机制前,值得指出的是:简单的请求/回应类型的Web服务,与客户端/服务器端的远程过程调用(RPC)方式不同。在RPC中,服务器给出一个类似API的接口。而客户端通过使用远程调用服务的API,传递必要的参数和接收调用值来启用一个服务。
   
  基于XMl的RPC调用类似在OO系统中调用一个对象一样。事实上,当你用基于XML的JAVA API工作时(Java API for XML-based RPC简JAX-RPC),你总是觉得在跟Java对象工作,而不是跟XML文档工作。JAX-RPC能让你把Web服务看作远程对象,这很像Java RMI(Remote Method Invocation)。JAX-RPC运行时把高层次的OO方法调用转化为远程Web服务需要XML文档。虽然RPC类型的Web服务通常能提供更便捷的编程模型,但是RPC调用也必须依赖更低的消息层来完成组成远程调用XML信息交换。 网管网bitsCN_com
  
  对于有些Web服务来说,直接在低级别的消息层编程通常是很有用的。例如,如果你想调用一个处理订单的文档并返回收据,对这样的Web服务,你能够很容易地把文档交换模块化为一个请求/回应的信息交换。替代远程方法调用的是,你将构建XML信息,直接发送这些信息到Web服务,如果有的话,还会处理服务的XML回应。由于SOAP为Web信息服务定义了公共的信息格式,你需要构建符合SOAP格式的信息,并且,一旦服务有回应,就要把这些SOAP回应信息解析成你的程序所能理解的格式。
  
  SAAJ提供了一个能方便地构建和读取SOAP信息的库,这个库还允许你通过网络接收和发送SOAP信息。SAAJ定义了javax.xml.soap包。在这个包里面的类组成了最初的JAXM(Java API for XML Messaging),但是最近他们被分离出来成为单独的API。JAXM依赖于SAAJ来构建和处理SOAP信息,并且依赖SAAJ来增加信息的可靠性和增加其他的XML信息特征。尽管SAAJ是J2EE的一部分,JAXM却不是。这篇文章聚焦于SAAJ中最有用的一个方面:就是SAAJ捆绑二进制内容到SOAP信息的能力。
  
  使用附件的好处
  尽管SOAP的设计核心集中在信息中封装XML文档,但是,SOAP的附件特征扩展了SOAP所能包含的信息,除了普通的SOAP部分,还可以包括0个到多个附件,如图1所示。每一个附近都由一个MIME类型定义,并且能采用任何的二进制流内容。

54com.cn


   
  图1:带有附件的SOAP信息
  当客户端希望传送二进制数据的时候,SOAP的附件特征是非常有用的,比如传送图像,音频数据给一个Web服务。如果没有SOAP附件,发送一个二进制信息会困难很多。比如,一个客户端的SOAP信息传送二进制文件的URL地址。那么,客户端不得不操作一个HTTP服务器,来让Web服务找到这个二进制文件。这会给Web服务的客户端造成过度的负载,特别是当客户端运行在资源有限的设备中,比如数码相机或者扫描仪。SOAP的附件能力使得任何的Web服务客户,能够在SOAP信息里直接传送内嵌二进制文件的SOAP信息。
  
  SOAP附件,已被证明能很方便地与网络站点入口交互。考虑这样一个现实中的房产代理网络,它需要发送房子的介绍和照片到房产查询中心入口。如果这个入口操作一个servlet来让带附件的SOAP信息置入,一个房产代理可以用几个SOAP信息更新它的列表,包括这些房子的照片。SOAP信息体可能嵌入了房子所有权的介绍,并且SOAP附件能带有房子图片文件。在这样的情况下,当一个入口操作servlet接收了这种信息,它就能返回一个认证文档,表明了在入口的发布的有效性。图2说明了这样的一个Web服务。
  

网管联盟www.bitsCN.com


  房产代理的WEB服务,使用了带附件的SOAP信息

带有附件的soap分析
    带有附件的SOAP信息并没有给SOAP增加新的特征。 确切的说,它定义了如何利用在SOAP信息中MIME类型来定义附件, 并且还定义了如何引用在SOAP体(SOAP Body)中的那些附件。
    MIME类型的复合块/关联(multipart/related)特性能定义由多部分组成的一个文档。带有附件的SOAP信息一定要符合这样的复合块/关联(multipart/related)的MIME类型。下面的例子展示了一个复合块/关联的 SOAP 信息,它被绑定到到 HTTP 协议,带有两个附件:


POST /propertyListing HTTP/1.1Host: www.realproperties.comContent-Type: Multipart/Related; boundary=MIME_boundary; type=text/XML; start="<property1234@realhouses.com>"Content-Length: NNNN--MIME_boundaryContent-Type: text/xml; charset=UTF-8Content-Transfer-Encoding: 8bitContent-ID: <property1234.xml@realhouses.com><?xml version='1.0'?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body>    <realProperty:propertyListing id="property_432"      xmlns:realProperty="http://schemas.realhouses.com/listingSubmission">       <listingAgency>Really Nice Homes, Inc.</listingAgency>      <listingType>Add</listingType>      <propertyAddress>        <street>1234 Main St</street>        <city>Pleasantville</city>        <state>CA</state>                 <zip>94323</zip>      </propertyAddress>       <listPrice>         250000       </listPrice>      <frontImage href="property1234_front.jpeg@realhouses.com"/>       <interiorImage href="property1234_interior.jpeg@realhouses.com"/>    </realProperty:propertyListing></SOAP-ENV: Body></SOAP-ENV: Envelope>--MIME_boundaryContent-Type: image/jpegContent-ID: ....JPEG DATA .....--MIME_boundaryContent-Type: image/jpegContent-ID: ....JPEG DATA .....--MIME_boundary--

    上述的复合块信息包含一系列的MIME头和相关的数据。文件的底层是SOAP体(SOAP Body)。 因为SOAP体只包含XML数据,整个信息的MIME类型是本文/xml(text/xml)类型 。 在SOAP封套(SOAP envelope)后面是二个附件,每个附件都包含一个连同信息一起发送的图像文件。
    用内容ID(Content ID)来识别每一个附件。W3C 备忘录允许用内容ID或内容位置来引用附件,但是它优先选择前者。这样的一个内容ID作为统一资源标志符URI(Uniform Resource Identifier)引用给附件;SOAP 1.1的编码规则定义了如何通过URI来引用SOAP信息里面的任何资源,不仅仅是引用XML( 参考SOAP1.1第5节资源)。当SOAP处理机处理信息时,它会解析这些URI引用。在上述的例子中,SOAP处理器把元素frontImage关联到内容ID为property1234_front.jpeg@realhouses.com的数据段中。

创建并发送带有附件的soap信息

    SAAJ能让你创建并编辑SOAP信息的任何部份, 包括附件。 大多数的SAAJ以抽象类和接口为基础,所以每个供应商都能实现它自己的SAAJ产品。Sun Microsystems公司的参考实现附在JWSDP包(Java Web Services Developer Pack)中。
    因为SOAP信息只是XML文档的一种特殊形式,JAAS在DOM(Document Object Model)API的基础上处理XML。大多数的SOAP信息组件派生自 avax.xml.soap.Node接口, 而这个接口又是org.w3c.dom.Node的子类。SAAJ继承了Node来添加SOAP样式的结构。 举例来说,这个特别的Node, SOAPElement,代表了一个SOAP信息元素。
    SAAJ依赖于接口和抽象类的直接结果是:你要通过工厂方法(factory methods)来完成大多数与SOAP相关的工作。 要把你的程序链接到SAAJ API,你首先要创建一个来自工厂方法SOAPConnectionFactory的链接SOAPConnection。要创建和编辑SOAP信息,你可以初始化MessageFactory和SOAPFactory。MessageFactory能让你产生SOAP信息,而 SOAPFactory则提供方法产生SOAP信息的各个部份:


SOAPConnectionFactory spConFactory = SOAPConnectionFactory.newInstance();SOAPConnection con = spConFactory.createConnection();SOAPFactory soapFactory = SOAPFactory.newInstance();


    把这些工具用在适当的位置,你就可以创建一个 SOAP 信息,在前面的例子中,来自房产代理的客户可以使用这些信息发送项目表更新给一个网站入口。
    SAAJ 提供了几个方法来产生一个新的 SOAP 信息。 下面例子演示了用最简单的方法来创建一个有封套(envelope)的空白SOAP信息,这个封套还带有头(heade)和体(body)。如果你在这个信息中不需要SOAP头(SOAP header),那么你可以将这个元素从信息中删除:


SOAPMessage message = factory.createMessage();SOAPHeader header = message.getSOAPHeader();header.detachNode();


    把 XML 结构加入信息也是直接了当的:


SOAPBody body = message.getSOAPBody();Name listingElementName = soapFactory.createName(    "propertyListing", "realProperty",     "http://schemas.realhouses.com/listingSubmission");SOAPBodyElement listingElement = body.addBodyElement(listingElementName);Name attname = soapFactory.createName("id");listingElement.addAttribute(attname, "property_1234");SOAPElement listingAgency = listingElement.addChildElement("listingAgency");listingAgency.addTextNode("Really Nice Homes, Inc");SOAPElement listingType = listingElement.addChildElement("listingType");listingType.addTextNode("add");SOAPElement propertyAddress = listingElement.addChildElement("propertyAddress");SOAPElement street = propertyAddress.addChildElement("street");street.addTextNode("1234 Main St");SOAPElement city = propertyAddress.addChildElement("city");city.addTextNode("Pleasantville");SOAPElement state = propertyAddress.addChildElement("state");state.addTextNode("CA");SOAPElement zip = propertyAddress.addChildElement("zip");zip.addTextNode("94521");SOAPElement listPrice = listingElement.addChildElement("listPrice");listPrice.addTextNode("25000");


   注意,你要把属性的ID作为一个参数加入到属性列表元素(propertyListing)中。 更进一步的是,你要用QName, 或namespace来限定propertyListing元素。

   你可以用几种方法把附件加入到SOAP信息中。 在这一个例子中,你首先要创建元素来指示列表属性的背景图片和前景图片。它们每个都有一个href属性指明附件的内容ID:


String frontImageID = "property1234_front_jpeg@realhouses.com";SOAPElement frontImRef = listingElement.addChildElement("frontImage");Name hrefAttName = soapFactory.createName("href");frontImRef.addAttribute(hrefAttName, frontImageID);String interiorID = "property1234_interior_jpeg@realhouses.com";SOAPElement interiorImRef = listingElement.addChildElement("interiorImage");interiorImRef.addAttribute(hrefAttName, interiorID);

    要方便地把需要的图像文件附加在信息中,可以使用JavaBeans Activation Framework架构里面的javax.activation.DataHandler对象。DataHandler能自动地检测传递给它的数据类型,而且它还能自动地分配适当的MIME类型给附件:


URL url = new URL("file:///eXPort/files/pic1.jpg");DataHandler dataHandler = new DataHandler(url);AttachmentPart att = message.createAttachmentPart(dataHandler);att.setContentId(frontImageID);message.addAttachmentPart(att);


    另一种方法,你可以把一个对象,连同正确的MIME类型一起,传递到createAttachmentPart()方法里。这个方法跟第一个方法类似。在内部,SAAJ将会寻找处理器DataContentHandler 来处理这个MIME类型。 如果它不能找一个合适的处理器,createAttachmentPart() 方法将会抛出一个IllegalArgumentException异常:


URL url2 = new URL("file:///export/files/pic2.jpg");Image im  = Toolkit.getDefaultToolkit().createImage(url2);AttachmentPart att2 = message.createAttachmentPart(im, "image/jpeg");att2.setContentId(interiorID);message.addAttachmentPart(att2);


    这个方法的缺点集中在:它依赖于AWT中示例UI(用户界面user interface)相关的类。 在一些粗心的(服务器)设置中,这些库并没有被正确的设置。
    不管你选择什么方法来产生附件,上述的代码表示了在第一个列表里面的SOAP信息。 既然这只是一个简单的XML信息,你可以用任何Web服务器能够接收的方法来发送这个消息: 一个直接的HTTP连接,JMX(JAVA管理扩展Java Management Extensions),JavASPaces, SMTP(简单邮件传输协议Simple Mail Transfer Protocol),等等。 SAAJ 提供了简单的方法来通过HTTP协议发送和接收SOAP信息。你只需要指定要发送的信息和连接终端的URL即可:


URL end point =    new URL("http://localhost:8080/saajtest/servlet/InvServlet");SOAPMessage response = connection.call(message, end point);


    SOAP连接的call()方法是同步的; 它会挂起直到它接收到回应。 回应以SOAP信息的形式发送过来。

用saaj接收并处理soap 信息

    读取和处理一个 SOAP 信息和创建一个这样的信息相似。在这一个例子中, servlet监听进入的SOAP信息,然后使用SAAJ来决定该如何处理它们。下面的代码段演示了用另外一种方法来构造 SOAP信息: 通过传递一组MIME头(MIME headers)和一个输入流到SOAP信息工厂来构造信息。 因此,在创建一个SOAP信息之前,你一定要解析HTTP请求的MIME头:


public void doPost(HttpServletRequest req, HttpServletResponse res)       throws ServletException, IOException {      ...MimeHeaders mimeHeaders = new MimeHeaders();Enumeration en = req.getHeaderNames();while (en.hasMoreElements()) {    String headerName = (String)en.nextElement();    String headerVal = req.getHeader(headerName);    StringTokenizer tk = new StringTokenizer(headerVal, ",");    while (tk.hasMoreTokens()){        mimeHeaders.addHeader(headerName, tk.nextToken().trim());    }}SOAPMessage message =    messageFactory.createMessage(mimeHeaders, req.getInputStream());

    通过那个SOAP信息,servlet能处理流入的信息。 要取回并发送公司的名字,比如,你或许首先要获得SOAP体的元素然后获得公司名字相应的元素:


SOAPBody body = message.getSOAPBody();Name listingElName = soapFactory.createName(     "propertyListing", "realProperty",     "http://schemas.realhouses.com/listingSubmission");Iterator listings = body.getChildElements(listingElName);while (listings.hasNext()) {      // The listing and its ID     SOAPElement listing = (SOAPElement)listings.next();     String listingID = listing.getAttribute("id");     // The listing agency name     Iterator ageIt = listing.getChildElements(soapFactory.createName("listingAgency"));     SOAPElement listingAgency = (SOAPElement)ageIt.next();     String agencyName = listingAgency.getValue();      ...}

    每个附件相关的内容ID使得Servlet能够从SOAP里取得需要的图片。这一个例子中,已经做了广告的属性的前景图片被保存在文件系统中。附件的MIME内容的类型决定了分配给保存文件的扩展名:


Iterator frontImage = listing.getChildElements(soapFactory.createName("frontImage"));SOAPElement front = (SOAPElement)frontImage.next();if (front != null) {    String frontID = front.getAttribute("href");    Iterator attachments = message.getAttachments();    while (attachments.hasNext()) {        AttachmentPart att = (AttachmentPart)attachments.next();        if (att.getContentId().equals(frontID)) {               String contentType = att.getContentType();             String[] parts = contentType.split("/");               String fileExt = "UNK";               if (parts.length > 0) {                 fileExt = parts[1];             }             InputStream is = att.getDataHandler().getInputStream();             FileOutputStream os =                  new FileOutputStream("/tmp/" + listingID+ "_front." + fileExt);             byte[] buff = new byte[1024];             int read = 0;             while ((read = is.read(buff, 0, buff.length)) != -1) {                 os.write(buff, 0, read);             }             os.flush();             os.close();        }     }   }

     一旦servlet取得图像并且处理了SOAP信息的XML内容,它就通过HTTP请求输出给客户一个答复。由于客户会在这个回应中寻找MIME头的类型,你必须在HTTP输出流中指定MIME头的值:


SOAPMessage reply = messageFactory.createMessage();SOAPHeader header = reply.getSOAPHeader();header.detachNode();SOAPBody replyBody = reply.getSOAPBody();SOAPBodyElement bodyElement = replyBody.addBodyElement(   soapFactory.createName("ack"));bodyElement.addTextNode("OK");res.setHeader("Content-Type", "text/xml");OutputStream os = res.getOuputStream();reply.writeTo(os);os.flush();


    当客户接收到确认信息的时候, 它能够处理这个信息,这跟服务器处理客户信息的方式是一样的。

    对用这种信息交换的一个告诫: 客户端和服务器端一定要知道每个信息的结构。 因为一个网络服务提供者总是把他的信息定义在Web服务注册表中,以Web服务描述语言(Web Services Description Language WSDL)文件的形式定义,所以客户端的程序员能检查服务的 WSDL 定义并且根据它构造信息。

在soap中发送二进制的附件? 没有问题

    开发者时常感觉用SOAP完成基于XML的远程过程调用过分的复杂。 在这一篇文章中,你已经看到能以简单的方式使用SOAP发送、接收简单的信息,并且这个SOAP信息不仅能包含XML内容,还能包含二进制内容。 由于支持创造和解析SOAP文件的工具的不断增多, 在SOAP上构建的一个简单的文件-交换Web服务是非常方便的。
    如这一篇文章所说的, SAAJ没有把构造和解析SOAP信息绑定到任何特定的信息传输通道。这样的无关性能让你自由选择最适合你程序的信息交换方式。比如,如果你需要容错的信息传输,你可能经由JAXM,Java信息服务(JMS)或JavaSpaces来传输SOAP信息。另一方面,如果你不需要招惹麻烦的高可靠性设计,就可能选择通过简单的HTTP连接经发送SOAP信息。


冬临 ,java 爱好者,Matrixxml soap翻译小组成员

资料引用:http://www.knowsky.com/349723.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值