JAX-WS 文件传递

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而言,具有下面的优势:

  1. SwA使用Base64binary传输,而MTOM使用ray binary传输。Base64binary比ray binary要多出33%的长度。

  2. MTOM在Base64binary与raw binary之间的转化是透明的,转化发生在临近wire两端,并不影响程序员的对SOAP消息的更改。程序员不需要关心。

  3. MTOM使用了现有的信息安全标准WS-Encryption和WS-Signatures,而SwA还需要自己照顾安全问题。

虽然MTOM具有传输优势,但对于小文件,过度的使用MTOM优化传输所带来的开销反而大于其传输所减少的时间。所以,对于MTOM可以设置一个值,当文件大小大于某值时,才开启MTOM。

借用下图,表示MTOM的工作方式。

JAX-WS 文件传递_第1张图片

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

  1. @BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_MTOM_BINDING) 为soap1.1开启MTOM

  2. @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之间的转换

JAX-WS 文件传递_第2张图片

在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, "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目录下。

在运行完程序以后,可以查看服务器目录和下载目录,文件应该已经在那里了。

你可能感兴趣的:(webservice,Attachment,jaxws,mtom)