在进入代码部分之前,还是贴出<<RESTful Web Service>>里对压缩的说明,毕竟这是本非常不错的书,而且书上说的比较清晰明了。

 

象XML文档这种文本格式的表示可以又很大的压缩率。HTTP客户端可以请求一个压缩版的表示,并为用户进行
透明的解压。其工作原理是这样的:客户端在发送HTTP请求时,在Accept-Ecoding包头里指出客户端支持那些
压缩算法。Accept-Encoding抱头又两个标准的值:compress和gzip

GET /resource.html HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip, compress

若服务器支持Accept-Ecoding请求报头里指出的压缩算法,那么它将先对表示进行压缩,然后再发给客户端。
Content-type响应报头的值不会因为服务器是否压缩而改变。但是服务器会再响应里附上一个
Content-Encoding报头,这样客户端就知道服务器返回的表示有没有压缩:

200 OK
Content-Type : text/html
Content-Encoding: gzip

客户端再收到服务器的响应后,将先用Content-Encoding响应报头里指出的算法来解压缩数据,然后按照
Content-Type响应报头指出的媒体类型来处理解压缩后的数据。通过采用压缩技术,只需增加少许复杂性就
可以节省大量带宽。

 

下面我们结合两种情况看看如何实现压缩(使用Restlet作为服务器和客户端):

 

1. 客户端向服务器发送GET请求,服务器返回一个压缩的表示给客户端

 

首先客户端发送请求,并指出客户端支持那些压缩算法:

 

Reference reference = new Reference("http://localhost:8080/restlet/resources/users/1");
Request get = new Request(Method.GET, reference);

List<Preference<Encoding>> encodings = new ArrayList<Preference<Encoding>>();
Preference<Encoding> pref = new Preference<Encoding>(Encoding.GZIP, 1.0f);
encodings.add(pref);
pref = new Preference<Encoding>(Encoding.COMPRESS, 0.5f);
encodings.add(pref);

get.getClientInfo().setAcceptedEncodings(encodings);

Client client = new Client(Protocol.HTTP);
Response response = client.handle(get);

 

/users/{userId}对应的资源是UserResource,那么服务器端接收到请求后,会做什么处理呢?服务器会首先判断客户端是否可以接受压缩的表示,并且支持那些算法,然后根据支持压缩算法的优先级别来决定使用那种算法来压缩表示。这里,为了测试方便,假设服务器端只支持GZIP算法,代码如下:

 

@Override
public void handleGet() {
	boolean isGZIP = false;
	List<Preference<Encoding>> encodings = getRequest().getClientInfo().getAcceptedEncodings();
	if (encodings != null) {
		for (Preference<Encoding> pre : encodings) {
			if (pre.getMetadata().equals(Encoding.GZIP)) {
				isGZIP = true;
				break;
			}
		}
	}
	Representation representation = null;
	if(isGZIP){
		representation = new EncodeRepresentation(Encoding.GZIP,
				new StringRepresentation(getUserXml(),
						MediaType.TEXT_PLAIN));
	}else{
		representation = new StringRepresentation(getUserXml(user), MediaType.TEXT_PLAIN);
	}
	
	getResponse().setStatus(Status.SUCCESS_OK);
	getResponse().setEntity(representation);	
}

 同样为了测试方便,我硬编码了一个压缩前的表示内容:

 

private String getUserXml(){
	StringBuffer userXml = new StringBuffer();
	
	userXml.append("<user id=\"1\">");
	userXml.append("<name>ajax</name>");
	userXml.append("<phone>13888888888</phone>");
	userXml.append("<email>ajax@iteye.com</email>");
	userXml.append("<address>Shanghai</address>");
	userXml.append("</user>");
	
	return userXml.toString();
}

 

至此,服务器端处理完成,那么客户端接收到服务器端返回的响应后,需要解压缩,当然了,需要首先判断服务器端使用的是那种压缩算法,看代码:

 

List<Encoding> _encodings = response.getEntity().getEncodings();
boolean isGZIP = false;

if (_encodings != null) {
	for (Encoding encoding : _encodings) {
		if (encoding.equals(Encoding.GZIP)) {
			isGZIP = true;
			break;
		}
	}
}
Representation rep = response.getEntity();
if(isGZIP){
	rep = new DecodeRepresentation(response.getEntity());
}

String testText = rep.getText();

 过程比较简单,不再多做解释。

 

2.客户端发送POST请求到服务器端,并且附送一个压缩的表示。

 

这种情况一般是客户端在请求时附送的表示非常耗费服务器资源,占用带宽,例如某些XML表示。为了测试,这里没有同服务器端反复协商的过程,直接发送使用GZIP压缩算法压缩过的表示。

 

Reference reference = new Reference("http://localhost:8080/restlet/resources/users/1");
Client client = new Client(Protocol.HTTP);

Representation representation = new EncodeRepresentation(Encoding.GZIP,
		new StringRepresentation(getUserXml(), MediaType.TEXT_PLAIN));

Response response = client.post(reference, representation);

assertTrue(response.getStatus().isSuccess());

 

看看服务器端怎么处理:

 

@Override
public void acceptRepresentation(Representation entity) throws ResourceException {
	try {
		String text = getRequest().getEntity().getText();
		
		System.out.println(text);
	} catch (IOException e1) {
		e1.printStackTrace();
	}
}

 

 咦,貌似不对,既然客户端压缩了表示,为什么服务器端没有解压缩? 是的,Restlet已经自动的做了解压缩,Restlet目前支持的压缩算法有:看DecodeRepresentation中的一段源代码:

 

public static List<Encoding> getSupportedEncodings() {
        return Arrays.<Encoding> asList(Encoding.GZIP, Encoding.DEFLATE,
                Encoding.ZIP, Encoding.IDENTITY);
    }

 

那假设服务器端和客户端有自己约定的压缩算法,而这个算法又不在Restlet支持之列,怎么处理这种情况?可以在Application的实例中禁止进行解压缩:

 

getDecoderService().setEnabled(false);

 

这样,就可以使用约定的压缩算法进行相应的压缩与解压缩了。