在用HttpClient抓取图片的过程中,提示如下错误:
Exception in thread "main" java.util.zip.ZipException: Not in GZIP format
at java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:165)
at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:79)
at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:91)
at org.apache.http.client.protocol.ResponseContentEncoding$1.create(ResponseContentEncoding.java:68)
at org.apache.http.client.entity.LazyDecompressingInputStream.initWrapper(LazyDecompressingInputStream.java:51)
at org.apache.http.client.entity.LazyDecompressingInputStream.read(LazyDecompressingInputStream.java:63)
at com.didichuxing.upload.TestUploadFile.main(TestUploadFile.java:53)
通过阅读异常堆栈的相关源码发现,由于HTTP请求头部信息包含“Content-Encoding: “compress,gzip””内容,导致HttpClient会根据“Content-Encoding”包装返回的InputStream,相关源码如下:
// 节选,详见org.apache.http.client.protocol.ResponseContentEncoding
final Header ceheader = entity.getContentEncoding();
// ……
// 有忽略
final InputStreamFactory decoderFactory = decoderRegistry.lookup(codecname);
所以相关的解决办法有两种。
1. 客户端解决办法
诊断HTTP请求,关键在于头部信息:
“Content-Encoding”, “compress,gzip”
但可惜的是,客户端没有修改HTTP头信息的能力(只有读的权限),所以必须在HttpClient用GZIP处理之前获取流,方法如下:
CloseableHttpClient httpclient = HttpClients.custom().addInterceptorFirst(new HttpResponseInterceptor() {
@Override
public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
HttpEntity resEntity = response.getEntity();
// 在此处消费InputStream
BufferedInputStream ins = new BufferedInputStream(resEntity.getContent());
FileOutputStream fos = new FileOutputStream("water1.png");
byte[] buffer = new byte[4096];
int len = 0;
while((len = ins.read(buffer)) > -1) {
fos.write(buffer, 0, len);
}
fos.close();
}
}).build();
总结下,几乎此错误的所有根源都来源于HttpClient的默认构造,如下:
// 通用的作用,但不适用于所有的情况
CloseableHttpClient httpclient = HttpClients.createDefault();
2. 服务端解决办法
服务端解决办法很简单,但唯一的问题在于,你需要有服务端修改代码的权限,如下:
// 不要启用此模式
// response.setHeader("Content-Encoding", "compress,gzip");
// 改为普通的流模式
response.setHeader("Content-Encoding", "application/octet-stream");
突然想起一个笑话:
专家吹牛,说天朝的神弹是如何如何的厉害,只需要一架战机飞到美帝白宫上空,即可将白宫炸为一片白地。那问题来了,战机怎么去哪里呢?
还有一种方法,就是服务器端真正支持GZip模式,详细请见《设置TOMCAT启用GZIP压缩》[我试了几次,似乎效果并不好,尤其是对图片的支持]。
3. 其他
利用JAVA HttpClient自动上传文件及抓取文件,详细内容见下:
1. HTTP客户端上传文件的办法
2. 改进的HTTP客户端上传文件的办法