MTOM是一种机制,用来以原始字节形式传输包含SOAP消息的较大二进制附件,从而使所传输的消息较小。
如果不用MTOM我们也可以通过WebService传递二进制的文件, 我们先来看看不用MTOM的时候是什么样子的。
服务端是一个简单的WebService方法, 接受一个byte数组,计算其中的字符数并返回。
@WebService public interface CounterService { int count(byte[] data); }
@Service @WebService(endpointInterface = "com.cccis.ws.CounterService") public class CounterServiceImpl implements CounterService { @Override public int count(byte[] data) { try { String text = GZipUtils.decompressText(data, "utf-8"); return text.length(); } catch (IOException e) { e.printStackTrace(); return -1; } } }
<bean id="countServiceImpl" class="com.cccis.ws.CounterServiceImpl" /> <jaxws:endpoint id="countService" implementor="#countServiceImpl" serviceName="countService" address="/countService" />
客户端是一个简单的WCF的客户端,读取一个文件把字节传递给count方法
try { var service = new ServiceReference1.CounterServiceClient(); var text = File.ReadAllText("c:\\words"); var memoryStream = new MemoryStream(); var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress); var rawData = Encoding.UTF8.GetBytes(text); gzipStream.Write(rawData, 0 , rawData.Length); gzipStream.Close(); var len = service.count(memoryStream.ToArray()); Console.WriteLine("lenght = {0}", len); } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); } Console.Read(); }
app.config配置如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="countServiceSoapBinding" /> </basicHttpBinding> </bindings> <client> <endpoint address="http://192.168.2.3:8080/binder/services/countService" binding="basicHttpBinding" bindingConfiguration="countServiceSoapBinding" contract="ServiceReference1.CounterService" name="CounterServiceImplPort" /> </client> </system.serviceModel> </configuration>
运行一些客户端,就可以让服务器计算字符数了, 我们来看看HTTP请求是什么样子的。
Content-Length: 1140036, 传输的内容都是base64编码的。也就是说传给count参数的byte数组被编码成Base64传输的。所以比实际的byte数组要大很多,我们的数组实际是854866, 大了近30%。
HTTP请求的内容
使用MTOM的情况
服务端
@WebService public interface CounterService { int count(@XmlMimeType("application/octet-stream") DataHandler value); }
@Service @WebService(endpointInterface = "com.cccis.ws.CounterService") public class CounterServiceImpl implements CounterService { @Override public int count(DataHandler data) { try { String text = GZipUtils.decompressText(data.getInputStream(), "utf-8"); return text.length(); } catch (IOException e) { e.printStackTrace(); return -1; } } }
Java接口的参数类型由原来的b yte[]变成 DataHanlder, 最重要的是要在前面加一个 @XmlMimeType("application/octet-stream")
配置文件:
<jaxws:endpoint id="countService" implementor="#countServiceImpl" serviceName="countService" address="/countService"> <jaxws:properties> <entry key="mtom-enabled" value="true" /> </jaxws:properties> </jaxws:endpoint>
添加了一个属性 mtom-enabled=true
加了上面的变化就OK了。
再来看看WCF 客户端的变化
只需要配置文件app.config发生变化就行了
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <customBinding> <binding name="countServiceSoapBinding"> <mtomMessageEncoding messageVersion="Soap11"></mtomMessageEncoding> <httpTransport/> </binding> </customBinding> </bindings> <client> <endpoint address="http://192.168.2.3:8080/binder/services/countService" binding="customBinding" bindingConfiguration="countServiceSoapBinding" contract="ServiceReference1.CounterService" name="CounterServiceImplPort" /> </client> </system.serviceModel> </configuration>
由原来的basicHttpBinding变成customBinding,然后使用 mtomMessageEncoding即可。 这里需要注意一点的是如果不设置messageVersion="Soap11" 默认使用的是Soap1.2 但是在我的环境里CXF对Soap1.2+MTOM的方式支持的好像不是很好。 所有退而求其次用Soap1.1。
最后我们看一下MTOM的方式的HTTP请求到底是什么样子的
我们可以看到Content-Length: 855855 比原来不用MTOM的1140036小了不少。 SOAP消息的样子也和原来的有很大的不同。
本文的源代码在附件中