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 Tutorial的1.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。
参考文档:
本文为笔者原创,转载请说明出处。