发送http请求给后端服务,在数据量大的时候报错:java
org.apache.http.ConnectionClosedException: Premature end of Content-Length delimited message body (expected: 144445481; received: 25370248)
意思是数据传输被提早终止了,指望传输144445481 B,只是传输了25370248 B。
http请求模型见上,在client用server的ip加端口访问服务,即便数据量大也没有问题。可是一旦经过域名访问,也就是经过Nginx访问,数据量大就会报上述错误。其实是Nginx对响应结果作了限制,若是Nginx超时时间设置的较短,而buffer很大的状况下,能够修改Nginx超时时间,将时间调大,这样在大数据量传输的过程当中就不会由于超时而终止。若是Nginx超时时间足够长,可是buffer较小的话,能够调大buffer来解决。
还有一点须要注意,就是同步异步的问题可能会引起这个问题,好比:后端
method A{异步
语句1;大数据
语句2(异步调用其余数据库查询);.net
语句3(finally块关闭链接)server
}
在上面的逻辑中,若是方法A是同步调用,可是数据库查询是异步,可能的一种状况是:数据没有传输完成,可是语句3已经执行了,链接被关闭,也可能引起上面的问题。
网上查到的解决方案,设置http传输协议版本,改为1.0,这样消息体就会一次性全部传过来;
HttpPost post = new HttpPost(builderUrl(url, requestParams));
post.setProtocolVersion(HttpVersion.HTTP_1_0);
笔者问题。通过上述方法并未得到解决
原因
找到了上传文件InputStream不完整的原因,是因为网络传输文件的全部字节,可能会分几批进行发送。
InputStream的available()只是其中的一部分字节。
解决方法:
拿到全部的字节总数,再进行传输。可以使用apache的IOUtils来处理(由于网络极为糟糕的,这个方法也会出错。):
byte[] readBuffer = IOUtils.toByteArray(inStream);
inStream = new ByteArrayInputStream(readBuffer);
由于网络情况极为糟糕的情况下,上述方法也会出错,笔者又在两次请求之间添加了间隔时间,情况好像有所改善。另外也不一定非要使用IOUtils.toByteArray,传输不好,多数是网络状况导致的。
附贴上笔者的部分代码
CloseableHttpResponse httpResponse = null;
CloseableHttpClient httpClient = null;
HttpEntity entity;
// 这里自定义了一个线程随机等待时间的方法,随机等待50-90秒
mySleep(50, 90);
try {
httpResponse = httpClient.execute(httpReq, localContext);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (httpResponse == null) {
// 如果请求不到,进行重试。
System.out.println("execute异常,重试....");
// 线程等待
mySleep(5, 7);
try {
httpResponse = httpClient.execute(httpReq, localContext);
} catch (IOException e) {
e.printStackTrace();
}
}
}
statusCode = httpResponse != null ? httpResponse.getStatusLine().getStatusCode() : 0;
// 再试一次
if (statusCode == 0) {
System.out.println("execute异常,再次重试....");
try {
httpResponse = httpClient.execute(httpReq, localContext);
} catch (IOException e) {
e.printStackTrace();
}
}
读取数据
public static byte[] readInputStream(InputStream inStream) {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
// 创建一个Buffer字符串
byte[] buffer = new byte[4096];
// 每次读取的字符串长度,如果为-1,代表全部读取完毕
try {
int len = 0;
// 使用一个输入流从buffer里把数据读取出来
while ((len = inStream.read(buffer)) != -1) {
// 用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度
// 每次把读取的内容,输出到目标路径文件中
outStream.write(buffer, 0, len);
}
} catch (IOException e) {
System.out.println("写入出错,重试");
readInputStream(inStream);
e.printStackTrace();
} finally {
try {
// 关闭输入流
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 把outStream里的数据写入内存
return outStream.toByteArray();
}
使用(部分代码摘取):
entity = httpResponse.getEntity();
InputStream inputStream = entity.getContent();
if (inputStream != null) {
data = readInputStream(inputStream);
}
OutputStream outputStream = new FileOutputStream(sf.getPath() + "\\" + filename);
outputStream.write(data);
上面代码进行了多次重试和多次等待。在网络极为糟糕的情况下有所改善。