如果ChunkedWriteHandler与HttpContentCompressor都使用,在传递文件会忽略掉HttpContentCompressor导致内容并没有压缩,所以重载HttpContentCompressor使得它可以与ChunkedWriteHandler配合使用。
以下代码中重载write函数将msg包装成HttpContent使得内容可以被压缩,同时重载beginEncode只容许压缩几种常见的类型,其他类型都放弃压缩,比如jpg、png等本身已经压缩过,再次压缩只会增大体积。
public class HttpChunkableContentCompressor extends HttpContentCompressor {
private static final String[] COMPRESSABLE = new String[] {
"/javascript", "text/css", "text/html", "text/plain", "text/csv"};
public HttpChunkableContentCompressor(int min, CompressionOptions... compressionOptions) {
super(min, compressionOptions);
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Object m = msg;
if (msg instanceof ByteBuf) {
/*
* 将ByteBuf转换为HttpContent,
* 使其能够使传递给HttpContentCompressor,
* 否则,在有ChunkedWriteHandler时,无法压缩文件
*/
ByteBuf buf = (ByteBuf) msg;
if (buf.isReadable()) {
//只编码非空缓冲区,因为空缓冲区可用于确定内容何时被刷新
m = new DefaultHttpContent(buf);
}
}
super.write(ctx, m, promise);
}
//注意以下代码与网上其他解决方案不同,重载beginEncode过滤掉无需压缩的文件类型
@Override
protected Result beginEncode(HttpResponse headers, String acceptEncoding) throws Exception {
String contentType = headers.headers().get(HttpUtil.HEAD_CONTENT_TYPE);
char c = contentType.charAt(0);
if(c == 't' || c == 'a') { //text or application
for(String ct : COMPRESSABLE) {
if(contentType.contains(ct)) {
return super.beginEncode(headers, acceptEncoding);
}
}
}
//Needn't, return null to disable compression
return null;
}
}
最后在pipeline中添加handler
ChannelPipeline pl = ctx.pipeline();
pl.addLast("http2_codec", builder.build());
//pl.addLast("aggregator", new HttpObjectAggregator(MAX_CONTENT_LENGTH));//无需聚集,h2在AbsServerRequest中完成
pl.addLast("compressor", new HttpChunkableContentCompressor(MIN_COMPRESS_LENGTH, new CompressionOptions[0]));
//提供chunk方式文件下载,比如DownloadLog中
pl.addLast("chunkedWriter", new ChunkedWriteHandler());
pl.addLast("http2_handler", http2ServerHandler);
最近(2023.5)发现在安卓环境,压缩不能使用。