MTOM可以在SOAP消息中传输二进制数据,与SAAJ传输附件不同,MTOM需要XOP来传输二进制数据。MTOM允许将消息中包含的大型数据元素外部化,并将其作为无任何特殊编码的二进制数据随消息一起发送。MTOM消息会打包为多部分相关MIME序列,放在SOAP消息中一起发送。因此你可以看出MTOM并不是将附件转为Base64编码,这样可以大大的提高性能,因为二进制文件转Base64编码会非常庞大。
package com.nantian.service; import java.util.Date; import javax.activation.DataHandler; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlMimeType; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "Customer") @XmlAccessorType(XmlAccessType.FIELD) public class Customer { private long id; private String name; private Date birthday; @XmlMimeType("application/octet-stream") private DataHandler imageData; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public DataHandler getImageData() { return imageData; } public void setImageData(DataHandler imageData) { this.imageData = imageData; } }
这里我们看到MTOM方式中要传输的附件必须使用javax.xml.bind.annotation.XmlMimeType进行注解,标注这是一个附件类型的数据,这里我们标注imageData是一个二进制数据,当然你也可以使用具体的MIME类型,譬如:image/jpg、image/gif等,但你要考虑客户端是否有相对应的类型。
这里你要注意的是必须在类上使用javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlMimeType.FIELD)注解,标注JAXB在进行java对象与XML之间进行转换时只关注字段,而不关注属性。
接下来你要分别在服务端和客户端分别启用MTOM支持,Spring的配置文件如下所示:
<jaxws:properties> <entry key="mtom-enabled" value="true"> </jaxws:properties>
这段内容加到<jaxws:server...、<jaxws:endpoint...、<jaxws:client...之间即可,也就是作为他们的子元素存在。如果你想使用java code实现,你可以在服务端、客户端获取javax.xml.ws.soap.SoapBinding实例,然后调用它的setMTOMEnabled(true)方法。其实从这里你可以看出JAX-WS是天然支持MTOM的,只不过默认禁用了这一功能,因为在没有附件这种大量数据要传输,MTOM的优点并不会体现出来。
我们假设服务端SEI的实现的一个方法如下所示:
public Customer selectMaxLongNameStudent(Customer c1, Customer c2) { Customer rs=null; if(c1.getName().length()>c2.getName().length()) rs=c1; else rs=c2; rs.setImageData(new DataHandler(new FileDataSource(new File("d:"+File.separator+"18.jpg")))); return rs; }
我们看到DataHandler需要DataSource进行构造,这里我们用到了javax.activation.DataSource的一个文件实现类来实现。
客户端调用代码如下所示:
...... String attachmentMimeType = helloServiceImpl.selectMaxLongNameStudent(c1, c2).getImageData().getDataSource().getContentType(); System.out.println(attachmentMimeType);
你可以看到控制台输出image/jpeg,MTOM传输附件成功。如果你使用了日志拦截器,你会看到服务端的控制台打印出了很多乱码,这些乱码就是传输附件。
如果我们禁用MTOM,还要传递附件,此时,附件会被编为BASE64码进行传递,这种方式传递附件的缺点很明显,一个10KB的图片的BASE64码在WORD里都可以用4篇来显示,那么大一些的附件将会使得XML的体积迅速膨胀,这与MTOM的原样传输二进制数据是没有可比性的。