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的方法有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);
本实例将实现一个文件上传与下载服务,为了展示不同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); }
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, "jpg", 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目录下。
在运行完程序以后,可以查看服务器目录和下载目录,文件应该已经在那里了。