http client fluent 报错案例

@RequestMapping(value = "/upload")  
    public String upload(@RequestParam(value = "file", required = false) MultipartFile file) {  
        String uploadResult = Request.Post("http://img.g3000.net/file/upload")
                .body(MultipartEntityBuilder.create()
                        .addBinaryBody("file", file.getInputStream(), ContentType.APPLICATION_OCTET_STREAM, "file").build())
                .execute().returnContent().asString();
    }

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

[ INFO] (RetryExec.java:94) - I/O exception (java.net.SocketException) caught when processing request to {}->http://img.g3000.net:80: Connection reset by peer: socket write error
[ERROR] (ImageService.java:55) - 上传图片异常
org.apache.http.client.ClientProtocolException
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:186)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
at org.apache.http.client.fluent.Request.execute(Request.java:143)
at com.g3000.model.service.ImageService.upload(ImageService.java:53)
at com.g3000.controller.FileUpload.uploadUserPhoto(FileUpload.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:777)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:706)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.g3000.filter.G3000CorsFilter.filterImpl(G3000CorsFilter.java:62)
at com.g3000.filter.G3000CorsFilter.doFilter(G3000CorsFilter.java:42)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1085)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1556)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1513)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.http.client.NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:105)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
... 53 more
Caused by: java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)
at java.net.SocketOutputStream.write(SocketOutputStream.java:153)
at org.apache.http.impl.io.SessionOutputBufferImpl.streamWrite(SessionOutputBufferImpl.java:123)
at org.apache.http.impl.io.SessionOutputBufferImpl.flushBuffer(SessionOutputBufferImpl.java:135)
at org.apache.http.impl.io.SessionOutputBufferImpl.flush(SessionOutputBufferImpl.java:142)
at org.apache.http.impl.io.ChunkedOutputStream.flush(ChunkedOutputStream.java:194)
at org.apache.http.entity.mime.content.StringBody.writeTo(StringBody.java:182)
at org.apache.http.entity.mime.AbstractMultipartForm.doWriteTo(AbstractMultipartForm.java:150)
at org.apache.http.entity.mime.AbstractMultipartForm.writeTo(AbstractMultipartForm.java:173)
at org.apache.http.entity.mime.MultipartFormEntity.writeTo(MultipartFormEntity.java:97)
at org.apache.http.impl.execchain.RequestEntityProxy.writeTo(RequestEntityProxy.java:116)
at org.apache.http.impl.DefaultBHttpClientConnection.sendRequestEntity(DefaultBHttpClientConnection.java:155)
at org.apache.http.impl.conn.CPoolProxy.sendRequestEntity(CPoolProxy.java:149)
at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:236)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:121)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:254)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
... 55 more
[ INFO] (MemcachedClientFactoryBean.java:62) - [{QA sa=/192.168.6.19:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0}]
[ INFO] (MemcachedClientFactoryBean.java:62) - [{QA sa=/192.168.6.19:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0}]


根据异常信息"Cannot retry request with a non-repeatable request entity.  The cause lists the reason the original request failed."可以确定抛出异常的原因是程序中使用了不可重复的请求实体(non-repeatable request entity)。
于是查看官方 http://hc.apache.org/httpcomponents-client-ga/tutorial/html/index.htmlHttpClient Tutorial的1.1.4.1. Repeatable entities http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d5e113节,内容如下:
引用

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。修改代码如下:

@RequestMapping(value = "/upload")  
    public String upload(@RequestParam(value = "file", required = false) MultipartFile file) {  
        String uploadResult = Request.Post("http://img.g3000.net/file/upload")
                .body(MultipartEntityBuilder.create()
                        .addBinaryBody("file", file.getBytes(), ContentType.APPLICATION_OCTET_STREAM, "file")
                       .build())
                .execute().returnContent().asString();
    }


验证:

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.jdom2.JDOMException;

import java.io.*;

public class TestMultipartForm {
    public static void main(String[] args) throws IOException, JDOMException {
        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost("http://10.5.13.21/upload/");

        MultipartEntity multipartEntity = new MultipartEntity();
        multipartEntity.addPart("pixel_middle", new StringBody("960,600"));
        multipartEntity.addPart("pixel_small", new StringBody("192,120"));
        multipartEntity.addPart("system", new StringBody("01"));
        multipartEntity.addPart("sort", new StringBody("01"));
        multipartEntity.addPart("uploader", new StringBody("0283"));
        multipartEntity.addPart("image", new InputStreamBody(new FileInputStream(new File("D:/httpclient/images/0.jpg");), file.getName()));
        
        // 显示实体是否可重复
        System.out.println("Repeatable:" + multipartEntity.isRepeatable());
        
        httpPost.setEntity(multipartEntity);
        HttpResponse response = httpclient.execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (200 == statusCode) {
                HttpEntity entity = response.getEntity();
                System.out.println(EntityUtils.toString(entity));
            } else {
                System.out.println("Error " + statusCode + "!");
            }
        } finally {
            httpPost.releaseConnection();
        }
    }
}



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

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。

猜你喜欢

转载自qiyuxi.iteye.com/blog/2352312