使用Android的OkHttp包实现基于HTTP协议的文件上传下载

 更新时间:2016年07月13日 14:51:03   作者:总李写代码  
OkHttp(GitHub主页https://github.com/square/okhttp)是近来人气攀升的一款安卓第三方HTTP包,这里我们来讲解一下如何使用Android的OkHttp包实现基于HTTP协议的文件上传下载:

OkHttp的HTTP连接基础
虽然在使用 OkHttp 发送 HTTP 请求时只需要提供 URL 即可,OkHttp 在实现中需要综合考虑 3 种不同的要素来确定与 HTTP 服务器之间实际建立的 HTTP 连接。这样做的目的是为了达到最佳的性能。
首先第一个考虑的要素是 URL 本身。URL 给出了要访问的资源的路径。比如 URL http://www.baidu.com 所对应的是百度首页的 HTTP 文档。在 URL 中比较重要的部分是访问时使用的模式,即 HTTP 还是 HTTPS。这会确定 OkHttp 所建立的是明文的 HTTP 连接,还是加密的 HTTPS 连接。
第二个要素是 HTTP 服务器的地址,如 baidu.com。每个地址都有对应的配置,包括端口号,HTTPS 连接设置和网络传输协议。同一个地址上的 URL 可以共享同一个底层 TCP 套接字连接。通过共享连接可以有显著的性能提升。OkHttp 提供了一个连接池来复用连接。
第三个要素是连接 HTTP 服务器时使用的路由。路由包括具体连接的 IP 地址(通过 DNS 查询来发现)和所使用的代理服务器。对于 HTTPS 连接还包括通讯协商时使用的 TLS 版本。对于同一个地址,可能有多个不同的路由。OkHttp 在遇到访问错误时会自动尝试备选路由。
当通过 OkHttp 来请求某个 URL 时,OkHttp 首先从 URL 中得到地址信息,再从连接池中根据地址来获取连接。如果在连接池中没有找到连接,则选择一个路由来尝试连接。尝试连接需要通过 DNS 查询来得到服务器的 IP 地址,也会用到代理服务器和 TLS 版本等信息。当实际的连接建立之后,OkHttp 发送 HTTP 请求并获取响应。当连接出现问题时,OkHttp 会自动选择另外的路由进行尝试。这使得 OkHttp 可以自动处理可能出现的网络问题。当成功获取到 HTTP 请求的响应之后,当前的连接会被放回到连接池中,提供给后续的请求来复用。连接池会定期把闲置的连接关闭以释放资源。

文件上传和下载实例:
1.不带参数上传文件

  /**
   * 上传文件
   * @param actionUrl 接口地址
   * @param filePath 本地文件地址
   */
  public <T> void upLoadFile(String actionUrl, String filePath, final ReqCallBack<T> callBack) {
    //补全请求地址
    String requestUrl = String.format("%s/%s", BASE_URL, actionUrl);
    //创建File
    File file = new File(filePath);
    //创建RequestBody
    RequestBody body = RequestBody.create(MEDIA_OBJECT_STREAM, file);
    //创建Request
    final Request request = new Request.Builder().url(requestUrl).post(body).build();
    final Call call = mOkHttpClient.newBuilder().writeTimeout(50, TimeUnit.SECONDS).build().newCall(request);
    call.enqueue(new Callback() {
      @Override
      public void onFailure(Call call, IOException e) {
        Log.e(TAG, e.toString());
        failedCallBack("上传失败", callBack);
      }

      @Override
      public void onResponse(Call call, Response response) throws IOException {
        if (response.isSuccessful()) {
          String string = response.body().string();
          Log.e(TAG, "response ----->" + string);
          successCallBack((T) string, callBack);
        } else {
          failedCallBack("上传失败", callBack);
        }
      }
    });
  }

2.带参数上传文件

/**
   *上传文件
   * @param actionUrl 接口地址
   * @param paramsMap 参数
   * @param callBack 回调
   * @param <T>
   */
  public <T>void upLoadFile(String actionUrl, HashMap<String, Object> paramsMap, final ReqCallBack<T> callBack) {
    try {
      //补全请求地址
      String requestUrl = String.format("%s/%s", upload_head, actionUrl);
      MultipartBody.Builder builder = new MultipartBody.Builder();
      //设置类型
      builder.setType(MultipartBody.FORM);
      //追加参数
      for (String key : paramsMap.keySet()) {
        Object object = paramsMap.get(key);
        if (!(object instanceof File)) {
          builder.addFormDataPart(key, object.toString());
        } else {
          File file = (File) object;
          builder.addFormDataPart(key, file.getName(), RequestBody.create(null, file));
        }
      }
      //创建RequestBody
      RequestBody body = builder.build();
      //创建Request
      final Request request = new Request.Builder().url(requestUrl).post(body).build();
      //单独设置参数 比如读取超时时间
      final Call call = mOkHttpClient.newBuilder().writeTimeout(50, TimeUnit.SECONDS).build().newCall(request);
      call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
          Log.e(TAG, e.toString());
          failedCallBack("上传失败", callBack);
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
          if (response.isSuccessful()) {
            String string = response.body().string();
            Log.e(TAG, "response ----->" + string);
            successCallBack((T) string, callBack);
          } else {
            failedCallBack("上传失败", callBack);
          }
        }
      });
    } catch (Exception e) {
      Log.e(TAG, e.toString());
    }
  }

3.带参数带进度上传文件

 /**
   *上传文件
   * @param actionUrl 接口地址
   * @param paramsMap 参数
   * @param callBack 回调
   * @param <T>
   */
  public <T> void upLoadFile(String actionUrl, HashMap<String, Object> paramsMap, final ReqProgressCallBack<T> callBack) {
    try {
      //补全请求地址
      String requestUrl = String.format("%s/%s", upload_head, actionUrl);
      MultipartBody.Builder builder = new MultipartBody.Builder();
      //设置类型
      builder.setType(MultipartBody.FORM);
      //追加参数
      for (String key : paramsMap.keySet()) {
        Object object = paramsMap.get(key);
        if (!(object instanceof File)) {
          builder.addFormDataPart(key, object.toString());
        } else {
          File file = (File) object;
          builder.addFormDataPart(key, file.getName(), createProgressRequestBody(MEDIA_OBJECT_STREAM, file, callBack));
        }
      }
      //创建RequestBody
      RequestBody body = builder.build();
      //创建Request
      final Request request = new Request.Builder().url(requestUrl).post(body).build();
      final Call call = mOkHttpClient.newBuilder().writeTimeout(50, TimeUnit.SECONDS).build().newCall(request);
      call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
          Log.e(TAG, e.toString());
          failedCallBack("上传失败", callBack);
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
          if (response.isSuccessful()) {
            String string = response.body().string();
            Log.e(TAG, "response ----->" + string);
            successCallBack((T) string, callBack);
          } else {
            failedCallBack("上传失败", callBack);
          }
        }
      });
    } catch (Exception e) {
      Log.e(TAG, e.toString());
    }
  }

4.创建带进度RequestBody

 /**
   * 创建带进度的RequestBody
   * @param contentType MediaType
   * @param file 准备上传的文件
   * @param callBack 回调
   * @param <T>
   * @return
   */
  public <T> RequestBody createProgressRequestBody(final MediaType contentType, final File file, final ReqProgressCallBack<T> callBack) {
    return new RequestBody() {
      @Override
      public MediaType contentType() {
        return contentType;
      }

      @Override
      public long contentLength() {
        return file.length();
      }

      @Override
      public void writeTo(BufferedSink sink) throws IOException {
        Source source;
        try {
          source = Okio.source(file);
          Buffer buf = new Buffer();
          long remaining = contentLength();
          long current = 0;
          for (long readCount; (readCount = source.read(buf, 2048)) != -1; ) {
            sink.write(buf, readCount);
            current += readCount;
            Log.e(TAG, "current------>" + current);
            progressCallBack(remaining, current, callBack);
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    };
  }

5.不带进度文件下载  

 /**
   * 下载文件
   * @param fileUrl 文件url
   * @param destFileDir 存储目标目录
   */
  public <T> void downLoadFile(String fileUrl, final String destFileDir, final ReqCallBack<T> callBack) {
    final String fileName = MD5.encode(fileUrl);
    final File file = new File(destFileDir, fileName);
    if (file.exists()) {
      successCallBack((T) file, callBack);
      return;
    }
    final Request request = new Request.Builder().url(fileUrl).build();
    final Call call = mOkHttpClient.newCall(request);
    call.enqueue(new Callback() {
      @Override
      public void onFailure(Call call, IOException e) {
        Log.e(TAG, e.toString());
        failedCallBack("下载失败", callBack);
      }

      @Override
      public void onResponse(Call call, Response response) throws IOException {
        InputStream is = null;
        byte[] buf = new byte[2048];
        int len = 0;
        FileOutputStream fos = null;
        try {
          long total = response.body().contentLength();
          Log.e(TAG, "total------>" + total);
          long current = 0;
          is = response.body().byteStream();
          fos = new FileOutputStream(file);
          while ((len = is.read(buf)) != -1) {
            current += len;
            fos.write(buf, 0, len);
            Log.e(TAG, "current------>" + current);
          }
          fos.flush();
          successCallBack((T) file, callBack);
        } catch (IOException e) {
          Log.e(TAG, e.toString());
          failedCallBack("下载失败", callBack);
        } finally {
          try {
            if (is != null) {
              is.close();
            }
            if (fos != null) {
              fos.close();
            }
          } catch (IOException e) {
            Log.e(TAG, e.toString());
          }
        }
      }
    });
  }

6.带进度文件下载

 /**
   * 下载文件
   * @param fileUrl 文件url
   * @param destFileDir 存储目标目录
   */
  public <T> void downLoadFile(String fileUrl, final String destFileDir, final ReqProgressCallBack<T> callBack) {
    final String fileName = MD5.encode(fileUrl);
    final File file = new File(destFileDir, fileName);
    if (file.exists()) {
      successCallBack((T) file, callBack);
      return;
    }
    final Request request = new Request.Builder().url(fileUrl).build();
    final Call call = mOkHttpClient.newCall(request);
    call.enqueue(new Callback() {
      @Override
      public void onFailure(Call call, IOException e) {
        Log.e(TAG, e.toString());
        failedCallBack("下载失败", callBack);
      }

      @Override
      public void onResponse(Call call, Response response) throws IOException {
        InputStream is = null;
        byte[] buf = new byte[2048];
        int len = 0;
        FileOutputStream fos = null;
        try {
          long total = response.body().contentLength();
          Log.e(TAG, "total------>" + total);
          long current = 0;
          is = response.body().byteStream();
          fos = new FileOutputStream(file);
          while ((len = is.read(buf)) != -1) {
            current += len;
            fos.write(buf, 0, len);
            Log.e(TAG, "current------>" + current);
            progressCallBack(total, current, callBack);
          }
          fos.flush();
          successCallBack((T) file, callBack);
        } catch (IOException e) {
          Log.e(TAG, e.toString());
          failedCallBack("下载失败", callBack);
        } finally {
          try {
            if (is != null) {
              is.close();
            }
            if (fos != null) {
              fos.close();
            }
          } catch (IOException e) {
            Log.e(TAG, e.toString());
          }
        }
      }
    });
  }

7.接口ReqProgressCallBack.java实现
public interface ReqProgressCallBack<T> extends ReqCallBack<T>{
  /**
   * 响应进度更新
   */
  void onProgress(long total, long current);
}
8.进度回调实现
  /**
   * 统一处理进度信息
   * @param total  总计大小
   * @param current 当前进度
   * @param callBack
   * @param <T>
   */
  private <T> void progressCallBack(final long total, final long current, final ReqProgressCallBack<T> callBack) {
    okHttpHandler.post(new Runnable() {
      @Override
      public void run() {
        if (callBack != null) {
          callBack.onProgress(total, current);
        }
      }
    });
  }

相关文章

  • 详解Flutter中key的正确使用方式

    详解Flutter中key的正确使用方式

    这篇文章主要为大家介绍了详解Flutter中key的正确使用方式,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • Android利用GridView实现单选效果

    Android利用GridView实现单选效果

    本篇文章主要介绍了Android利用GridView实现单选效果的相关知识,具有很好的参考价值。下面跟着小编一起来看下吧
    2017-05-05
  • android viewpaper实例探讨

    android viewpaper实例探讨

    本文将提供一个android viewpaper实例实现过程,需要了解更多的朋友可以参考下
    2012-12-12
  • Android图片三级缓存开发

    Android图片三级缓存开发

    这篇文章主要为大家详细介绍了Android图片三级缓存开发的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • android获取当前手机号示例程序

    android获取当前手机号示例程序

    这篇文章主要介绍了android如何获取当前手机号的方法,大家参考使用吧
    2013-11-11
  • Android编程Widget创建与使用方法简明教程

    Android编程Widget创建与使用方法简明教程

    这篇文章主要介绍了Android编程Widget创建与使用方法,结合实例形式分析了Widget的功能、使用方法与相关注意事项,需要的朋友可以参考下
    2016-10-10
  • Android 完整购物商城界面的实现案例

    Android 完整购物商城界面的实现案例

    这篇文章为大家带来一个Android 完整购物商城的界面具体的实现,案例中含有商品列表的显示,为商城最重要的功能之一,感兴趣的朋友来看看吧
    2022-03-03
  • Android编程设计模式之策略模式详解

    Android编程设计模式之策略模式详解

    这篇文章主要介绍了Android编程设计模式之策略模式,结合实例形式详细分析了Android策略模式的概念、原理、实现方法及相关注意事项,需要的朋友可以参考下
    2017-12-12
  • 在Android上实现HttpServer的示例代码

    在Android上实现HttpServer的示例代码

    本篇文章主要介绍了在Android上实现HttpServer的示例代码,实现Android本地的微型服务器,具有一定的参考价值,有兴趣的可以了解一下
    2017-08-08
  • android 获取屏幕尺寸

    android 获取屏幕尺寸

    这篇文章主要介绍了android 获取屏幕尺寸的方法,需要的朋友可以参考下
    2015-03-03

最新评论