HttpClient - NonRepeatableRequestException

在测试HttpClient编写的图片上传程序时抛出以下异常:

 1 Exception in thread "main" org.apache.http.client.ClientProtocolException
 2     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:909)
 3     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
 4     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:784)
 5     at TestImageServer.main(TestImageServer.java:75)
 6 Caused by: org.apache.http.client.NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity.  The cause lists the reason the original request failed.
 7     at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:686)
 8     at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:517)
 9     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
10     ... 3 more
11 Caused by: java.io.IOException: Read error
12     at java.io.FileInputStream.readBytes(Native Method)
13     at java.io.FileInputStream.read(FileInputStream.java:177)
14     at org.apache.http.entity.mime.content.InputStreamBody.writeTo(InputStreamBody.java:69)
15     at org.apache.http.entity.mime.HttpMultipart.doWriteTo(HttpMultipart.java:206)
16     at org.apache.http.entity.mime.HttpMultipart.writeTo(HttpMultipart.java:224)
17     at org.apache.http.entity.mime.MultipartEntity.writeTo(MultipartEntity.java:183)
18     at org.apache.http.entity.HttpEntityWrapper.writeTo(HttpEntityWrapper.java:98)
19     at org.apache.http.impl.client.EntityEnclosingRequestWrapper$EntityWrapper.writeTo(EntityEnclosingRequestWrapper.java:108)
20     at org.apache.http.impl.entity.EntitySerializer.serialize(EntitySerializer.java:122)
21     at org.apache.http.impl.AbstractHttpClientConnection.sendRequestEntity(AbstractHttpClientConnection.java:271)
22     at org.apache.http.impl.conn.ManagedClientConnectionImpl.sendRequestEntity(ManagedClientConnectionImpl.java:197)
23     at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:257)
24     at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
25     at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:712)
26     ... 5 more

根据异常信息"Cannot retry request with a non-repeatable request entity.  The cause lists the reason the original request failed."可以确定抛出异常的原因是程序中使用了不可重复的请求实体(non-repeatable request entity)。HttpClient代码如下:

 1 import org.apache.http.HttpEntity;
 2 import org.apache.http.HttpResponse;
 3 import org.apache.http.client.HttpClient;
 4 import org.apache.http.client.methods.HttpPost;
 5 import org.apache.http.entity.mime.MultipartEntity;
 6 import org.apache.http.entity.mime.content.InputStreamBody;
 7 import org.apache.http.entity.mime.content.StringBody;
 8 import org.apache.http.impl.client.DefaultHttpClient;
 9 import org.apache.http.util.EntityUtils;
10 import org.jdom2.JDOMException;
11 
12 import java.io.*;
13 
14 /**
15  * Created with IntelliJ IDEA.
16  * User: qutengfei
17  * Date: 12-11-15
18  * Time: 下午2:00
19  * To change this template use File | Settings | File Templates.
20  * 测试 MultipartForm
21  */
22 public class TestMultipartForm {
23     public static void main(String[] args) throws IOException {
24         HttpClient httpclient = new DefaultHttpClient();
25         HttpPost httpPost = new HttpPost("http://10.5.13.21/upload/");
26 
27         MultipartEntity multipartEntity = new MultipartEntity();
28         multipartEntity.addPart("pixel_middle", new StringBody("960,600"));
29         multipartEntity.addPart("pixel_small", new StringBody("192,120"));
30         multipartEntity.addPart("system", new StringBody("01"));
31         multipartEntity.addPart("sort", new StringBody("01"));
32         multipartEntity.addPart("uploader", new StringBody("0283"));
33         multipartEntity.addPart("image", new InputStreamBody(new FileInputStream(new File("D:/httpclient/images/0.jpg");), file.getName()));
34 
35         httpPost.setEntity(multipartEntity);
36         HttpResponse response = httpclient.execute(httpPost);
37         try {
38             int statusCode = response.getStatusLine().getStatusCode();
39             if (200 == statusCode) {
40                 HttpEntity entity = response.getEntity();
41                 System.out.println(EntityUtils.toString(entity));
42             } else {
43                 System.out.println("Error " + statusCode + "!");
44             }
45         } finally {
46             httpPost.releaseConnection();
47         }
48     }
49 }

于是查看官方HttpClient Tutorial1.1.4.1. Repeatable entities节,内容如下:

An entity can be repeatable, meaning its content can be read more than once. This is only possible with self contained entities (like ByteArrayEntity or StringEntity)

根据上面对Repeatable Entity的介绍,就可以确定问题是因为构建multipartEntity时包含除ByteArrayEntity和StringEntity以外的Entity。修改代码如下:

 1 import org.apache.http.HttpEntity;
 2 import org.apache.http.HttpResponse;
 3 import org.apache.http.client.HttpClient;
 4 import org.apache.http.client.methods.HttpPost;
 5 import org.apache.http.entity.mime.MultipartEntity;
 6 import org.apache.http.entity.mime.content.InputStreamBody;
 7 import org.apache.http.entity.mime.content.StringBody;
 8 import org.apache.http.impl.client.DefaultHttpClient;
 9 import org.apache.http.util.EntityUtils;
10 import org.jdom2.JDOMException;
11 
12 import java.io.*;
13 
14 /**
15  * Created with IntelliJ IDEA.
16  * User: qutengfei
17  * Date: 12-11-15
18  * Time: 下午2:00
19  * To change this template use File | Settings | File Templates.
20  * 测试 MultipartForm
21  */
22 public class TestMultipartForm {
23     public static void main(String[] args) throws IOException, JDOMException {
24         HttpClient httpclient = new DefaultHttpClient();
25         HttpPost httpPost = new HttpPost("http://10.5.13.21/upload/");
26 
27         MultipartEntity multipartEntity = new MultipartEntity();
28         multipartEntity.addPart("pixel_middle", new StringBody("960,600"));
29         multipartEntity.addPart("pixel_small", new StringBody("192,120"));
30         multipartEntity.addPart("system", new StringBody("01"));
31         multipartEntity.addPart("sort", new StringBody("01"));
32         multipartEntity.addPart("uploader", new StringBody("0283"));
33         multipartEntity.addPart("image", new InputStreamBody(new FileInputStream(new File("D:/httpclient/images/0.jpg");), file.getName()));
34         
35         // 显示实体是否可重复
36         System.out.println("Repeatable:" + multipartEntity.isRepeatable());
37         
38         httpPost.setEntity(multipartEntity);
39         HttpResponse response = httpclient.execute(httpPost);
40         try {
41             int statusCode = response.getStatusLine().getStatusCode();
42             if (200 == statusCode) {
43                 HttpEntity entity = response.getEntity();
44                 System.out.println(EntityUtils.toString(entity));
45             } else {
46                 System.out.println("Error " + statusCode + "!");
47             }
48         } finally {
49             httpPost.releaseConnection();
50         }
51     }
52 }

编译执行上述程序,结果如下:

Repeatable:false

注释第33行代码,重新编译执行程序,结果如下:

Repeatable:true

根据上面的执行结果,可以确定InputStreamBody是造成multipartEntity为不可重复实体的元凶。

HttpClient有ByteArrayBody、FileBody、InputStreamBody和StringBody四种Body。经过笔者测试,其中只有用InputStreamBody构建multipartEntity才是non-repeatable entity。

总结:

笔者最早使用InputStreamBody而不是FileBody,是因为业务需要前置系统将图片上传到图片服务器,并保存图片链接地址到前置系统的数据库。但是前置系统是B/S架构,图片从页面上传到前置的后台是以流的方式,故而才使用InputStreamBody。最后,使用ByteArrayBody代替InputStreamBody。

参考文档:

TestMultipartContentBody

TestMultipartForm

TestMultipartFormHttpEntity

HttpCore API

HttpClient API

本文为笔者原创,转载请说明出处。

posted @ 2012-11-16 10:28  清若止水  阅读(13904)  评论(0编辑  收藏  举报