上篇文章我给出了如何在客户端和服务器端使用Restlet实现表示的压缩,这一篇将结合源代码看看Restlet的API是如何支持压缩的。
首先看解压缩,开发中的代码:
Representation rep = new DecodeRepresentation(response.getEntity()); String testText = rep.getText();
当然了,从response返回的Entity就是压缩的表示,所以把它塞到DEcodeRepresentation的构造函数里,看一下DecodeRepresentation构造函数做了些什么:
public DecodeRepresentation(Representation wrappedRepresentation) { super(wrappedRepresentation); this.canDecode = getSupportedEncodings().containsAll( wrappedRepresentation.getEncodings()); this.wrappedEncodings = new ArrayList<Encoding>(); this.wrappedEncodings.addAll(wrappedRepresentation.getEncodings()); }
上面这段代码首先会判断当前表示的压缩算法在Restlet里是否支持,注意,这个参数很重要,如果这里判断是不支持的算法,则后续代码就不会解压缩,实际上也不知道该如何解压缩。其余的几行代码就是保存一下参数。
当所有参数设置完成以后,在客户端调用getText想得到解压缩后的表示,以做进一步的处理。
@Override public String getText() throws IOException { String result = null; if (canDecode()) { result = ByteUtils.toString(getStream(), getCharacterSet()); } else { result = getWrappedRepresentation().getText(); } return result; }
上述这段代码也就是我之前所说的首先会判断是否是支持的压缩算法,这里,如果是直接的压缩算法,也就是可以解压缩,就走解压缩的流程,否则,直接从包裹的表示里面取出表示的内容。代码里调用了getStream方法,我继续深入进去看一下:
@Override public InputStream getStream() throws IOException { InputStream result = null; if (canDecode()) { result = getWrappedRepresentation().getStream(); for (int i = this.wrappedEncodings.size() - 1; i >= 0; i--) { if (!this.wrappedEncodings.get(i).equals(Encoding.IDENTITY)) { result = getDecodedStream(this.wrappedEncodings.get(i), result); } } } return result; }
这段代码是根据表示的内容,返回一个Stream。在上述方法里调用了getDecodedStream,这个方法就是真正解压缩的方法,从下面这个方法,我闷也能看出,Restlet使用了Java提供的解压缩的类,包括的压缩类型:GZIP、ZIP、DEFLATE,而IDENTITY是缺省编码,表示不进行任何编码转换,所以,遇到这种情况,代码会抛出一个IOException。
private InputStream getDecodedStream(Encoding encoding, InputStream encodedStream) throws IOException { InputStream result = null; if (encodedStream != null) { if (encoding.equals(Encoding.GZIP)) { result = new GZIPInputStream(encodedStream); } else if (encoding.equals(Encoding.DEFLATE)) { result = new InflaterInputStream(encodedStream); } else if (encoding.equals(Encoding.ZIP)) { final ZipInputStream stream = new ZipInputStream(encodedStream); if (stream.getNextEntry() != null) { result = stream; } } else if (encoding.equals(Encoding.IDENTITY)) { throw new IOException( "Decoder unecessary for identity decoding"); } } return result; }
再来看看压缩的部分,客户端我们通常会这样写:
Representation representation = new EncodeRepresentation(Encoding.GZIP, new StringRepresentation(getUserXml(), MediaType.TEXT_PLAIN)); Response response = client.post(reference, representation);
当客户端第一行代码被调用时候,对应的源代码如下:
public EncodeRepresentation(Encoding encoding, Representation wrappedRepresentation) { super(wrappedRepresentation); this.canEncode = getSupportedEncodings().contains(encoding); this.encodings = null; this.encoding = encoding; }
代码比较简单,就是判断一下客户端指定的压缩算法Restlet是否支持,因为我们当前用Restlet作为请求的客户端,所以,采用的压缩算法,必须是Restlet支持的,否则,服务器端接收到表示后,也因为不支持压缩算法而不进行解压缩。
当客户端提交请求,如上述代码:client.post(reference, representation);,则EncodeRepresentation的write方法被调用:
@Override public void write(OutputStream outputStream) throws IOException { if (canEncode()) { DeflaterOutputStream encoderOutputStream = null; if (this.encoding.equals(Encoding.GZIP)) { encoderOutputStream = new GZIPOutputStream(outputStream); } else if (this.encoding.equals(Encoding.DEFLATE)) { encoderOutputStream = new DeflaterOutputStream(outputStream); } else if (this.encoding.equals(Encoding.ZIP)) { final ZipOutputStream stream = new ZipOutputStream(outputStream); if (getWrappedRepresentation().getDownloadName() != null) { stream.putNextEntry(new ZipEntry(getWrappedRepresentation() .getDownloadName())); } else { stream.putNextEntry(new ZipEntry("entry")); } encoderOutputStream = stream; } else if (this.encoding.equals(Encoding.IDENTITY)) { // Encoder unecessary for identity encoding } if (encoderOutputStream != null) { getWrappedRepresentation().write(encoderOutputStream); encoderOutputStream.finish(); } else { getWrappedRepresentation().write(outputStream); } } else { getWrappedRepresentation().write(outputStream); } }
这个方法所做的,与解压缩刚好相反,在解压缩时,我们通过使用Java标准的压缩类库,去得到一个解压缩后的输入流,那么与之对应的,在压缩时候,就需要将表示压缩成一个输出流,另外,根据客户端指定的压缩算法,采用不同Java压缩类来实现。当然,在write方法调用之前,还会调用本类或者父类的其它的一些方法,但是这个不是本篇关注的重点。
总结:结合上篇文章以及本篇,应该能明白Restlet是如何实现解压缩的,根据源代码的讲解,在设计客户端的时候,如何有效的避免一些陷阱,有效的、快速的设计客户端。