Xutils HttpUtils上传文件的实现

来源:互联网 发布:淘宝网秋冬运动套装 编辑:程序博客网 时间:2024/06/05 20:42

今天解决了一个网络请求方面的bug, 在解决之余 熟悉了一下我们项目中网络请求用到的框架, 目前用的还是比较老的xUtils来处理http请求,闲暇之余对这部分流程进行了一番跟踪并做一下记录, 方便日后记忆学习


在理解Utils实现上传功能的流程之前, 需要对另外一个东东有所了解---HttpClient.java.  它是Apache http包里的一个类, 利用此类也可以实现上传文件的功能, 具体实现如下:


[java] view plain copy
  1. public void upload(String localFile){  
  2.          File file = new File(localFile);  
  3.          PostMethod filePost = new PostMethod(URL_STR);  
  4.          HttpClient client = new HttpClient();  
  5.            
  6.          try {  
  7.              // 通过以下方法可以模拟页面参数提交  
  8.              filePost.setParameter("userName", userName);  
  9.              filePost.setParameter("passwd", passwd);  
  10.    
  11.              Part[] parts = { new FilePart(file.getName(), file) };  
  12.              filePost.setRequestEntity(new MultipartRequestEntity(parts, filePost.getParams()));  
  13.                
  14.              client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);  
  15.                
  16.              int status = client.executeMethod(filePost);  
  17.              if (status == HttpStatus.SC_OK) {  
  18.                  System.out.println("上传成功");  
  19.              } else {  
  20.                  System.out.println("上传失败");  
  21.              }  
  22.          } catch (Exception ex) {  
  23.              ex.printStackTrace();  
  24.          } finally {  
  25.              filePost.releaseConnection();  
  26.          }  
  27. }  

如上所述,通过HttpClient.executeMethod(PostMethod)的方式也可以实现上传文件的功能,  而在本文所讲的xUtils的上传功能实际上也是对HttpClient的封装, 提供统一接口,不必书写冗长的代码, 并添加了缓冲,重连,断点续传等功能。

以下代码是使用xUtils实现上传的demo

[java] view plain copy
  1. //实例化HttpUtils对象, 参数设置链接超时  
  2. HttpUtils HTTP_UTILS = new HttpUtils(60 * 1000);  
  3. //实例化RequestParams对象  
  4. RequestParams requestParams = new RequestParams();  
  5. //requestParams.setContentType("multipart/form-data");  
  6. StringBuilder picFileName = new StringBuilder();  
  7. picFileName.append(DeviceInfo.getInstance().getGetuiClientId())  
  8.            .append(NotifyUtil.getCurrentTime()).append(".png");  
  9. requestParams.addBodyParameter("picFileName", picFileName.toString());  
  10. //imageFile是File格式的对象, 将此File传递给RequestParams  
  11. requestParams.addBodyParameter("picFile", imageFile, "image/png");  
  12. requestParams.addBodyParameter("platform""Android");  
  13. String photoUrl = Config.getUploadPhotoUrl();  
  14. //通过HTTP_UTILS来发送post请求, 并书写回调函数  
  15. HTTP_UTILS.send(HttpMethod.POST, url, params, new com.lidroid.xutils.http.callback.RequestCallBack<String>() {  
  16.             @Override  
  17.             public void onFailure(HttpException httpException, String arg1) {  
  18.                   
  19.             }  
  20.   
  21.             @Override  
  22.             public void onSuccess(ResponseInfo<String> responseInfo) {  
  23.                   
  24.             }  
  25.         });  
  26. }  

可以看到使用方法就是创建一个RequestParams与一个Url, 然后通过xUtils中的HttpUtils.send方法发送请求上传imageFile这个文件,并通过RequestCallBack这个类来回调处理返回的数据, 接下来就看一下具体流程:

首先看先HttpUtils.java

[java] view plain copy
  1. public HttpUtils(int connTimeout, String userAgent) {  
  2.     HttpParams params = new BasicHttpParams();  
  3.   
  4.     ConnManagerParams.setTimeout(params, connTimeout);  
  5.     HttpConnectionParams.setSoTimeout(params, connTimeout);  
  6.     HttpConnectionParams.setConnectionTimeout(params, connTimeout);  
  7.   
  8.     if (TextUtils.isEmpty(userAgent)) {  
  9.         userAgent = OtherUtils.getUserAgent(null);  
  10.     }  
  11.     HttpProtocolParams.setUserAgent(params, userAgent);  
  12.   
  13.     ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(10));  
  14.     ConnManagerParams.setMaxTotalConnections(params, 10);  
  15.   
  16.     HttpConnectionParams.setTcpNoDelay(params, true);  
  17.     HttpConnectionParams.setSocketBufferSize(params, 1024 * 8);  
  18.     HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);  
  19.   
  20.     SchemeRegistry schemeRegistry = new SchemeRegistry();  
  21.     schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));  
  22.     schemeRegistry.register(new Scheme("https", DefaultSSLSocketFactory.getSocketFactory(), 443));  
  23.   
  24.     <span style="color:#FF0000;">//这个是核心类,最终就是调用这个HttpClient的方法去向服务端发送请求,上传文件</span>  
  25.     httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager(params, schemeRegistry), params);  
  26.   
  27.     httpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_RETRY_TIMES));  
  28.   
  29.     httpClient.addRequestInterceptor(new HttpRequestInterceptor() {  
  30.         @Override  
  31.         public void process(org.apache.http.HttpRequest httpRequest, HttpContext httpContext) throws org.apache.http.HttpException, IOException {  
  32.             if (!httpRequest.containsHeader(HEADER_ACCEPT_ENCODING)) {  
  33.                 httpRequest.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP);  
  34.             }  
  35.         }  
  36.     });  
  37.     httpClient.addResponseInterceptor(new HttpResponseInterceptor() {  
  38.          @Override  
  39.          public void process(HttpResponse response, HttpContext httpContext) throws org.apache.http.HttpException, IOException {  
  40.              final HttpEntity entity = response.getEntity();  
  41.              if (entity == null) {  
  42.                  return;  
  43.              }  
  44.              final Header encoding = entity.getContentEncoding();  
  45.              if (encoding != null) {  
  46.                  for (HeaderElement element : encoding.getElements()) {  
  47.                      if (element.getName().equalsIgnoreCase("gzip")) {  
  48.                          response.setEntity(new GZipDecompressingEntity(response.getEntity()));  
  49.                          return;  
  50.                      }  
  51.                  }  
  52.              }  
  53.          }  
  54.      });  
  55.  }  


就从RequestParams.java开始说起,它有几个重载的addBodyParameter的方法

当调用requestParams.addBodyParameter("picFile", imageFile, "image/png");时 看下具体代码:

[html] view plain copy
  1. <span style="font-size:18px;">public void addBodyParameter(String key, File file, String mimeType) {  
  2.     if (fileParams == null) {  
  3.         fileParams = new HashMap<String, ContentBody>();  
  4.     }  
  5.     fileParams.put(key, new FileBody(file, mimeType));  
  6. }</span>  

fileParams是一个内部变量,声明是 private HashMap<String, ContentBody> fileParams; 可见,当传入的参数是File时,参数被保存在fileParams这个HashMap当中。OK至此, RequestParams的初始化已经结束。 接下来就是调用HttpUtils.send的方法来像服务端发送请求

[java] view plain copy
  1. <span style="font-size:18px;">public <T> HttpHandler<T> send(HttpRequest.HttpMethod method, String url, RequestParams params,  
  2.                                RequestCallBack<T> callBack) {  
  3.     if (url == nullthrow new IllegalArgumentException("url may not be null");  
  4.   
  5.     HttpRequest request = new HttpRequest(method, url);  
  6.     return sendRequest(request, params, callBack);  
  7. }</span>  
通过method与url创建一个HttpRequest, 并调用sendRequest方法,首先来看一下这个HttpRequest

[java] view plain copy
  1. <span style="font-size:18px;">public HttpRequest(HttpMethod method, URI uri) {  
  2.     super();  
  3.     this.method = method;  
  4.     setURI(uri);  
  5. }</span>  
将method和url传给内部变量,之后在调用client执行request的时候会用到这些参数, 接下来回到sendRequest方法:

[java] view plain copy
  1. <span style="font-size:18px;">private <T> HttpHandler<T> sendRequest(HttpRequest request, RequestParams params, RequestCallBack<T> callBack) {  
  2.   
  3.     HttpHandler<T> handler = new HttpHandler<T>(httpClient, httpContext, responseTextCharset, callBack);  
  4.   
  5.     handler.setExpiry(currentRequestExpiry);  
  6.     handler.setHttpRedirectHandler(httpRedirectHandler);  
  7.     request.setRequestParams(params, handler);  
  8.   
  9.     if (params != null) {  
  10.         handler.setPriority(params.getPriority());  
  11.     }  
  12.     handler.executeOnExecutor(EXECUTOR, request);  
  13.     return handler;  
  14. }</span>  
又冒出一个HttpHandler类, 创建它用到了4个参数, 其中第一个是httpClient, 这个client就是HttpUtils初始化的时候实例化的DefaultHttpClient的对象。

然后调用request.setRequestParams方法, 如下

[java] view plain copy
  1. <span style="font-size:18px;">public void setRequestParams(RequestParams param) {  
  2.     if (param != null) {  
  3.         if (uriCharset == null) {  
  4.             uriCharset = Charset.forName(param.getCharset());  
  5.         }  
  6.         List<RequestParams.HeaderItem> headerItems = param.getHeaders();  
  7.         if (headerItems != null) {  
  8.             for (RequestParams.HeaderItem headerItem : headerItems) {  
  9.                 if (headerItem.overwrite) {  
  10.                     this.setHeader(headerItem.header);  
  11.                 } else {  
  12.                     this.addHeader(headerItem.header);  
  13.                 }  
  14.             }  
  15.         }  
  16.         this.addQueryStringParams(param.getQueryStringParams());  
  17.         this.setEntity(param.getEntity());  
  18.     }  
  19. }</span>  
参数中的params就是我们调用HttpUtils.send方式之前,实例化的HttpParams对象, 至此 在HttpRequest对象中已经有method,url, 以及HttpParams的封装,最后调用handler.executeOnExecutor(EXECUTOR, request);来执行此request。 HttpHandler实现了PriorityAsyncTask, 你懂的,最终会调用doInBackground,看一下实现:

[java] view plain copy
  1. <span style="font-size:18px;">protected Void doInBackground(Object... params) {  
  2.     if (this.state == State.CANCELLED || params == null || params.length == 0return null;  
  3.   
  4.     if (params.length > 3) {  
  5.         fileSavePath = String.valueOf(params[1]);  
  6.         isDownloadingFile = fileSavePath != null;  
  7.         autoResume = (Boolean) params[2];  
  8.         autoRename = (Boolean) params[3];  
  9.     }  
  10.   
  11.     try {  
  12.         if (this.state == State.CANCELLED) return null;  
  13.         // init request & requestUrl  
  14.         request = (HttpRequestBase) params[0];  
  15.         requestUrl = request.getURI().toString();  
  16.         if (callback != null) {  
  17.             callback.setRequestUrl(requestUrl);  
  18.         }  
  19.   
  20.         this.publishProgress(UPDATE_START);  
  21.   
  22.         lastUpdateTime = SystemClock.uptimeMillis();  
  23.   
  24.         ResponseInfo<T> responseInfo = sendRequest(request);  
  25.         if (responseInfo != null) {  
  26.             this.publishProgress(UPDATE_SUCCESS, responseInfo);  
  27.             return null;  
  28.         }  
  29.     } catch (HttpException e) {  
  30.         this.publishProgress(UPDATE_FAILURE, e, e.getMessage());  
  31.     }  
  32.   
  33.     return null;  
  34. }  
  35. </span>  
params[0]就是传进来的HttpRequest, 之后调用sendRequest的方法先服务端发送请求, 并返回ResponseInfo的对象, 代码如下:

[java] view plain copy
  1. <span style="font-size:18px;">private ResponseInfo<T> sendRequest(HttpRequestBase request) throws HttpException {  
  2.   
  3.     HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();  
  4.     while (true) {  
  5.   
  6.         if (autoResume && isDownloadingFile) {  
  7.             File downloadFile = new File(fileSavePath);  
  8.             long fileLen = 0;  
  9.             if (downloadFile.isFile() && downloadFile.exists()) {  
  10.                 fileLen = downloadFile.length();  
  11.             }  
  12.             if (fileLen > 0) {  
  13.                 request.setHeader("RANGE""bytes=" + fileLen + "-");  
  14.             }  
  15.         }  
  16.   
  17.         boolean retry = true;  
  18.         IOException exception = null;  
  19.         try {  
  20.             requestMethod = request.getMethod();  
  21.             if (HttpUtils.sHttpCache.isEnabled(requestMethod)) {  
  22.                 String result = HttpUtils.sHttpCache.get(requestUrl);  
  23.                 if (result != null) {  
  24.                     return new ResponseInfo<T>(null, (T) result, true);  
  25.                 }  
  26.             }  
  27.   
  28.             ResponseInfo<T> responseInfo = null;  
  29.             if (!isCancelled()) {  
  30.                 HttpResponse response = client.execute(request, context);  
  31.                 responseInfo = handleResponse(response);  
  32.             }  
  33.             return responseInfo;  
  34.         } catch (UnknownHostException e) {  
  35.             exception = e;  
  36.             retry = retryHandler.retryRequest(exception, ++retriedCount, context);  
  37.         } catch (IOException e) {  
  38.             exception = e;  
  39.             retry = retryHandler.retryRequest(exception, ++retriedCount, context);  
  40.         } catch (NullPointerException e) {  
  41.             exception = new IOException(e.getMessage());  
  42.             exception.initCause(e);  
  43.             retry = retryHandler.retryRequest(exception, ++retriedCount, context);  
  44.         } catch (HttpException e) {  
  45.             throw e;  
  46.         } catch (Throwable e) {  
  47.             exception = new IOException(e.getMessage());  
  48.             exception.initCause(e);  
  49.             retry = retryHandler.retryRequest(exception, ++retriedCount, context);  
  50.         }  
  51.         if (!retry) {  
  52.             throw new HttpException(exception);  
  53.         }  
  54.     }  
  55. }  
  56. </span>  
先获取下client.getHttpRequestRetryHandler(),获取retry的设置; 如果使用了缓存则通过requestUrl去httpCache去获取,获取到了则创建ResponseInfo对象。如果没有缓存,调用httpClient执行http请求,获取到得结果交由handleResponse处理。


至此,上传流程基本结束,做一下总结吧:

1 创建并实例化HttpUtils类,  在构造方法中会实例化最核心的类DefaultHttpClient对象---此对象用来执行request,向服务端发送上传请求,并返回结果

2 创建并实例化HttpParams对象, 通过addBodyParameter方法将要上传的File传给HttpParams

3 创建并实例化HttpHandler对象, 将DefaultHttpClient的实例通过参数形式,传给HttpHandler

4 创建并实例化HttpRequest对象,在此对象中,封装好了method, url, 以及params, 然后通过HttpHandler对象来执行此request,并最终调用DefaultHttpClient来执行此request, 返回HttpResponse

0 0
原创粉丝点击