我正在一个我目前只需要使用apache hc库的环境中工作。更具体地说,
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<version>4.1.3</version>
</dependency>
虽然我尝试使用CloseableHttpAsyncClient的实例作为Spring的AsyncRestTemplate的客户端。我编写了以下示例来试验从本地服务器获取gzip压缩内容。
我可以使用httpie验证服务器是否支持压缩响应,如下所示。
$ http --print=Hhb :8081/hello
GET /hello HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8081
User-Agent: HTTPie/0.9.9
HTTP/1.1 200
Content-Encoding: gzip
Content-Type: application/json;charset=UTF-8
Date: Tue, 06 Mar 2018 22:05:25 GMT
Transfer-Encoding: chunked
Vary: Accept-Encoding
[
1,
2,
3,
4,
5
]
现在,当我尝试使用CloseableHttpAsyncClient的示例代码进行读取时,它无法正常工作。即使在添加了ResponseInterceptor之后,我也需要解压缩gzip内容。
// org.slf4j.Logger LOGGER
// Imports
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
// Demo Code
CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom()
.setDefaultRequestConfig(
RequestConfig.custom()
.setContentCompressionEnabled(false)
.build())
.setDefaultHeaders(Collections.singleton(new BasicHeader("Accept-Encoding", "gzip")))
.setProxy(new HttpHost("localhost", 8080))
.addInterceptorFirst((HttpResponseInterceptor) (response, context) -> {
HttpEntity entity = response.getEntity();
Header contentEncodingHeader = entity.getContentEncoding();
if (contentEncodingHeader != null) {
HeaderElement[] encodings = contentEncodingHeader.getElements();
for (int i = 0; i < encodings.length; i++) {
if (encodings[i].getName().equalsIgnoreCase("gzip")) {
response.setEntity(new GzipDecompressingEntity(entity));
break;
}
}
}
})
.build();
httpAsyncClient.start();
HttpGet request = new HttpGet("http://localhost:8081/hello");
Future<HttpResponse> future = httpAsyncClient.execute(request, null);
HttpResponse httpResponse = future.get();
LOGGER.info(decompress(EntityUtils.toByteArray(httpResponse.getEntity())));
// Output:
// [1,2,3,4,5]
其中解压缩是我写的一个小实用程序来进行解压缩。
public static String decompress(byte[] compressedBytes) throws IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(compressedBytes);
GZIPInputStream gis = new GZIPInputStream(bis);
BufferedReader br = new BufferedReader(new InputStreamReader(gis, "UTF-8"));
StringBuilder sb = new StringBuilder();
String line;
while((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
gis.close();
bis.close();
return sb.toString();
}
现在我期望的是EntityUtils.toString(httpResponse.getEntity())
应该直接返回字符串中的未压缩响应,但它没有,我必须手动解压缩内容。我怀疑减压是GzipDecompressingEntity的工作,但由于某些原因它不能很好地发挥作用(可能是因为响应被分块了吗?)
有没有办法可以通过正确地使用GzipDecompressingEntity
或者以异步方式手动执行解压缩来以异步方式进行解压缩,这样当我future.get()
时,它的响应就是没压缩?