MTOM vs SwA
WebService传递文件有两种标准,SwA(SOAP with Attachment)和MTOM(Message Transmission and Optimization Mechanism)。SwA正在被性能更好的MTOM with XOP(XML Binary Optimized Packaging)所代替。如果不开启MTOM的话,则使用SwA。
MTOM相比较SwA而言,具有下面的优势:
SwA使用Base64binary传输,而MTOM使用ray binary传输。Base64binary比ray binary要多出33%的长度。
MTOM在Base64binary与raw binary之间的转化是透明的,转化发生在临近wire两端,并不影响程序员的对SOAP消息的更改。程序员不需要关心。
MTOM使用了现有的信息安全标准WS-Encryption和WS-Signatures,而SwA还需要自己照顾安全问题。
虽然MTOM具有传输优势,但对于小文件,过度的使用MTOM优化传输所带来的开销反而大于其传输所减少的时间。所以,对于MTOM可以设置一个值,当文件大小大于某值时,才开启MTOM。
借用下图,表示MTOM的工作方式。
MTOM开启方法
在服务器端,开启MTOM的方法有3
1, 在SEI实现类上使用@javax.xml.ws.soap.MTOM(threshold=1000). 此命令意思是对于1k以上的文件,开启MTOM。
2,在sun-jaxws.xml中声明<endpoint enable-mtom="true" .../>
3,在SEI实现类上使用@BindingType
@BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_MTOM_BINDING) 为soap1.1开启MTOM
@BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_MTOM_BINDING)?为soap1.2开启MTOM
在客户端,开启MTOM的方法有2
1,在创建Port的时候,使用MTOMFeature()作为参数。?
2,像下面的代码一样,使用SOAPBinding
Hello?port?=?new?HelloService.getHelloPort();
//get?the?binding?and?enable?mtom
SOAPBinding?binding?=?(SOAPBinding)?((BindingProvider)?port).getBinding();
boolean?mtomEnabled?=?binding.isMTOMEnabled();
binding.setMTOMEnabled(true);
除了开启MTOM以外,还应该注意MIME数据与Java之间的转换。MTOM支持以下转换:
JAXB xmime:expectedContentType与java type之间的转换
在wsimport对wsdl转型的时候,若想将xml element转换成对应的java type,必须声明xmime:expectedContentType. 如:
<xs:complexType?name="downloadImgResponse">
????<xs:sequence>
??????<xs:element?name="return"?xmime:expectedContentTypes="image/jpeg"?type="xs:base64Binary"?minOccurs="0"/>
????</xs:sequence>
??</xs:complexType>
在wsgen对java想wsdl转型的时候,若想将operation的参数和返回值转为相应的mine type,则需要使用annotation @XmlMimeType,如:
public?boolean?uploadImg(String?name,?@XmlMimeType("image/jpeg")?Image?img);
????
@XmlMimeType("image/jpeg")
public?Image?downloadImg(String?name);
MTOM实例
本实例将实现一个文件上传与下载服务,为了展示不同MIME类型不同的处理方式,在服务中将提供两类文件的上传与下载,image/jpeg和application/octet-stream.
package?com.mycompany;
import?java.awt.Image;
import?javax.activation.DataHandler;
import?javax.jws.WebService;
import?javax.xml.bind.annotation.XmlMimeType;
@WebService
public?interface?AttachmentEngine?{
????//?上传图片
????public?boolean?uploadImg(String?name,?@XmlMimeType("image/jpeg")?Image?img);
????//?下载图片
????@XmlMimeType("image/jpeg")
????public?Image?downloadImg(String?name);
????//?上传二进制文件
????public?boolean?uploadBinary(String?name,?@XmlMimeType("application/octet-stream")?DataHandler?handler);
????//?下载二进制文件
????@XmlMimeType("application/octet-stream")
????public?DataHandler?downloadBinary(String?name);
}
SEI实现类
package?com.mycompany;
import?java.awt.Image;
import?java.awt.image.RenderedImage;
import?java.io.File;
import?java.io.FileNotFoundException;
import?java.io.FileOutputStream;
import?java.io.IOException;
import?javax.activation.DataHandler;
import?javax.activation.FileDataSource;
import?javax.imageio.ImageIO;
import?javax.jws.WebService;
import?javax.xml.bind.annotation.XmlMimeType;
import?javax.xml.ws.WebServiceException;
import?javax.xml.ws.soap.MTOM;
@MTOM
@WebService(endpointInterface="com.mycompany.AttachmentEngine")
public?class?AttachmentEngineImpl?implements?AttachmentEngine?{
????private?final?File?saveDir?=?new?File("d:/share/server");
????//?将上传的图片以参数name命名,并存储到d:/share/server目录下。
????@Override
????public?boolean?uploadImg(String?name,?@XmlMimeType("image/jpeg")?Image?img)?{
????????try?{
????????????ImageIO.write((RenderedImage)?img,?"jpg",?new?File(saveDir,?name));
????????}?catch?(IOException?e)?{
????????????throw?new?WebServiceException("Fail?to?upload.");
????????}
????????return?true;
????}
????//?获取d:/share/server下名为name的图片
????@Override
????@XmlMimeType("image/jpeg")
????public?Image?downloadImg(String?name)?{
????????File?image?=?new?File(saveDir,?name);
????????try?{
????????????return?ImageIO.read(image);
????????}?catch?(IOException?e)?{
????????????throw?new?WebServiceException("Fail?to?download.",e);
????????}
????}
????//?将上传的文件以参数name命名,并存储到d:/share/server目录下。
????@Override
????public?boolean?uploadBinary(String?name,????????????@XmlMimeType("application/octet-stream")?DataHandler?handler)?{
????????try?{
????????????handler.writeTo(new?FileOutputStream(new?File(saveDir,?name)));
????????????return?true;
????????}?catch?(FileNotFoundException?e)?{
????????????throw?new?WebServiceException("Fail?to?upload.",e);
????????}?catch?(IOException?e)?{
????????????throw?new?WebServiceException("Fail?to?upload.",e);
????????}
????}
????//?获取d:/share/server下名为name的文件
????@Override
????@XmlMimeType("application/octet-stream")
????public?DataHandler?downloadBinary(String?name)?{
????????return?new?DataHandler(new?FileDataSource(new?File(saveDir,?name)));
????}
}
打包部署
创建sun-jaxws.xml
<?xml?version="1.0"?encoding="UTF-8"?>
<endpoints?xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime'?version='2.0'>
????<endpoint
????????name='attachment'
????????implementation='com.mycompany.AttachmentEngineImpl'
????????url-pattern='/service'/>
</endpoints>
然后打包成file.war,最后部署到tomcat。
访问http://localhost:8080/file/service?
生成客户端
首先使用wsimport生成客户端。最后写一个类来检验webservice是否工作。
public?class?App?{
????private?static?final?File?uploadDir?=?new?File("d:/share/client/upload");
????private?static?final?File?downloadDir?=?new?File("d:/share/client/download");
????
????public?static?void?main(?String[]?args?)?throws?IOException??{
????????//?生成一个开启MTOM的port
????????AttachmentEngine?engine?=?new?AttachmentEngineImplService().getAttachmentEngineImplPort(new?MTOMFeature());
????????//?将upload目录下的a.JPG以另一个名字photo.jpg上传
????????engine.uploadImg("photo.jpg",?ImageIO.read(new?File(uploadDir,?"a.JPG")));
????????//?从服务器端重新获取photo.jpg
????????Image?img?=?engine.downloadImg("photo.jpg");
????????//?将服务器端获取的photo.jpg以download.jpg的名字存到download目录中。
????????ImageIO.write((RenderedImage)?img,?new?File(downloadDir,?"download.jpg"));
????????//?上传b.pdf,改名为test.pdf
????????engine.uploadBinary("test.pdf",?new?DataHandler(new?FileDataSource(new?File(uploadDir,?"b.pdf"))));
????????//?下载test.pdf
????????DataHandler?handler?=?engine.downloadBinary("test.pdf");
????????//?将下载的test.pdf存入download目录
????????handler.writeTo(new?FileOutputStream(new?File(downloadDir,?"download.pdf")));
????}
}
在运行程序前,请先将a.JPG和b.pdf复制到upload目录下。
在运行完程序以后,可以查看服务器目录和下载目录,文件应该已经在那里了。