我们知道webservice调用生成的xml数据挺大的,如果能压缩传输可以显著减少网络延迟,提高系统的整体性能。那么ksoap2支持gzip压缩传输么?这就是我这两天要搞清楚的问题。
ksoap2用于数据发送接收的类是由继承抽象类org.ksoap2.transport.Transport实现的HttTransportSE实现的
通过研究HttpTransportSE的call方法List org.ksoap2.transport.HttpTransportSE.call(String soapAction, SoapEnvelope envelope, List headers, File outputFile)
可以发现HttpTransportSE可以接收gzip压缩的数据。如果收到的是gzip,会自动将数据解压缩,以下为call方法的代码片段(中文注释为本文作者添加):
retHeaders = connection.getResponseProperties();
for (int i = 0; i < retHeaders.size(); i++) {
HeaderProperty hp = (HeaderProperty)retHeaders.get(i);
// HTTP response code has null key
if (null == hp.getKey()) {
continue;
}
// If we know the size of the response, we should use the size to initiate vars
if (hp.getKey().equalsIgnoreCase("content-length") ) {
if ( hp.getValue() != null ) {
try {
contentLength = Integer.parseInt( hp.getValue() );
} catch ( NumberFormatException nfe ) {
contentLength = 8192;
}
}
}
// Check the content-type header to see if we're getting back XML, in case of a
// SOAP fault on 500 codes
if (hp.getKey().equalsIgnoreCase("Content-Type")
&& hp.getValue().contains("xml")) {
xmlContent = true;
}
// ignoring case since users found that all smaller case is used on some server
// and even if it is wrong according to spec, we rather have it work..
//判断是否为gzip压缩数据
if (hp.getKey().equalsIgnoreCase("Content-Encoding")
&& hp.getValue().equalsIgnoreCase("gzip")) {
gZippedContent = true;
}
}
//first check the response code....
if (status != 200) {
//throw new IOException("HTTP request failed, HTTP status: " + status);
throw new HttpResponseException("HTTP request failed, HTTP status: " + status, status);
}
if (contentLength > 0) {
if (gZippedContent) {
//对gzip压缩数据解压缩
is = getUnZippedInputStream(
new BufferedInputStream(connection.openInputStream(),contentLength));
} else {
is = new BufferedInputStream(connection.openInputStream(),contentLength);
}
}
那么发送数据呢?ksoap能不能对发送的请求数据进行gzip压缩?
下面是HttpTransportSE中发送请求数据的方法sendData(void org.ksoap2.transport.HttpTransportSE.sendData(byte[] requestData, ServiceConnection connection, SoapEnvelope envelope)
)的代码,很简单可以看出,发送数据并没有gzip压缩:
protected void sendData(byte[] requestData, ServiceConnection connection, SoapEnvelope envelope)
throws IOException
{
connection.setRequestProperty("Content-Length", "" + requestData.length);
connection.setFixedLengthStreamingMode(requestData.length);
OutputStream os = connection.openOutputStream();
os.write(requestData, 0, requestData.length);
os.flush();
os.close();
}
现在我们可以得出结论,ksoap2的代码并没有支持gzip发送请求,我下载了它的最新版本3.6.1.也依然没有支持。
ksoap2本身就是个轻量级的soap框架,并没有做到非常完善,但因为它的框架结构比较好,使用者完全可以根据自己的需要在ksoap2的代码基础上进行功能扩展。
所以要实现gzip压缩发送request,需要自己动手实现。其实实现也不复杂:
好在sendData方法是protected,所以在不修改ksoap2代码的基础上我们只要从HttpTransportSE继承新建一个类,然后重写sendData方法,以后就用自己写的这个类来负责数据的发送和接收就可以实现完全的gzip压缩数据传输.
下面是我的实现完整代码:
HttpTransportSEWithGzip.java
package net.gdface.service.client.ksoapstub;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.Proxy;
import java.util.zip.GZIPOutputStream;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.transport.HttpTransportSE;
import org.ksoap2.transport.ServiceConnection;
/** * 实现Gzip发送请求
* {@link #compress}为true时发送数据前先压缩数据 * @author guyadong * */
public class HttpTransportSEWithGzip extends HttpTransportSE {
/** * 是否启用GZIP压缩 */
public boolean compress=true;
public HttpTransportSEWithGzip(String url) {
super(url);
}
public HttpTransportSEWithGzip(Proxy proxy, String url) {
super(proxy, url);
}
public HttpTransportSEWithGzip(String url, int timeout) {
super(url, timeout);
}
public HttpTransportSEWithGzip(Proxy proxy, String url, int timeout) {
super(proxy, url, timeout);
}
public HttpTransportSEWithGzip(String url, int timeout, int contentLength) {
super(url, timeout, contentLength);
}
public HttpTransportSEWithGzip(Proxy proxy, String url, int timeout, int contentLength) {
super(proxy, url, timeout, contentLength);
}
@Override
protected void sendData(byte[] requestData, ServiceConnection connection, SoapEnvelope envelope) throws IOException {
if (this.compress) {
// 先将数据压缩再调用父类的sendData方法完成实际数据发送
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(1024);
GZIPOutputStream os = null;
try {
os = new GZIPOutputStream(arrayOutputStream);
os.write(requestData, 0, requestData.length);
os.finish();
} finally {
if (null != os)
os.close();
}
// 指定数据编码方式为gzip压缩
connection.setRequestProperty("Content-Encoding", "gzip");
requestData = arrayOutputStream.toByteArray();
}
// 调用父类的方法完成请求数据发送
super.sendData(requestData, connection, envelope);
}
}