热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

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

OkHttp(GitHub主页https:github.comsquareokhttp)是近来人气攀升的一款安卓第三方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  void upLoadFile(String actionUrl, String filePath, final ReqCallBack 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 
   */
  public void upLoadFile(String actionUrl, HashMap paramsMap, final ReqCallBack 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 
   */
  public  void upLoadFile(String actionUrl, HashMap paramsMap, final ReqProgressCallBack 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 
   * @return
   */
  public  RequestBody createProgressRequestBody(final MediaType contentType, final File file, final ReqProgressCallBack 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  void downLoadFile(String fileUrl, final String destFileDir, final ReqCallBack 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  void downLoadFile(String fileUrl, final String destFileDir, final ReqProgressCallBack 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 extends ReqCallBack{
  /**
   * 响应进度更新
   */
  void onProgress(long total, long current);
}
8.进度回调实现
  /**
   * 统一处理进度信息
   * @param total  总计大小
   * @param current 当前进度
   * @param callBack
   * @param 
   */
  private  void progressCallBack(final long total, final long current, final ReqProgressCallBack callBack) {
    okHttpHandler.post(new Runnable() {
      @Override
      public void run() {
        if (callBack != null) {
          callBack.onProgress(total, current);
        }
      }
    });
  }


推荐阅读
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • 本文详细探讨了HTTP 500内部服务器错误的成因、解决方案及其在Web开发中的影响。通过对具体案例的分析,帮助读者理解并解决此类问题。 ... [详细]
  • 在现代网络环境中,两台计算机之间的文件传输需求日益增长。传统的FTP和SSH方式虽然有效,但其配置复杂、步骤繁琐,难以满足快速且安全的传输需求。本文将介绍一种基于Go语言开发的新一代文件传输工具——Croc,它不仅简化了操作流程,还提供了强大的加密和跨平台支持。 ... [详细]
  • 解决微信电脑版无法刷朋友圈问题:使用安卓远程投屏方案
    在工作期间想要浏览微信和朋友圈却不太方便?虽然微信电脑版目前不支持直接刷朋友圈,但通过远程投屏技术,可以轻松实现在电脑上操作安卓设备的功能。 ... [详细]
  • 从零开始构建完整手机站:Vue CLI 3 实战指南(第一部分)
    本系列教程将引导您使用 Vue CLI 3 构建一个功能齐全的移动应用。我们将深入探讨项目中涉及的每一个知识点,并确保这些内容与实际工作中的需求紧密结合。 ... [详细]
  • 深入理解Java中的volatile、内存屏障与CPU指令
    本文详细探讨了Java中volatile关键字的作用机制,以及其与内存屏障和CPU指令之间的关系。通过具体示例和专业解析,帮助读者更好地理解多线程编程中的同步问题。 ... [详细]
  • 如何在PHPcms网站中添加广告
    本文详细介绍了在PHPcms网站后台添加广告的方法,涵盖多种常见的广告形式,如百度广告和Google广告,并提供了相关设置的步骤。同时,文章还探讨了优化网站流量的SEO策略。 ... [详细]
  • 本文详细介绍了如何使用Python编写爬虫程序,从豆瓣电影Top250页面抓取电影信息。文章涵盖了从基础的网页请求到处理反爬虫机制,再到多页数据抓取的全过程,并提供了完整的代码示例。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文介绍如何使用 Sortable.js 库实现元素的拖拽和位置交换功能。Sortable.js 是一个轻量级、无依赖的 JavaScript 库,支持拖拽排序、动画效果和多种插件扩展。通过简单的配置和事件处理,可以轻松实现复杂的功能。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 本文详细介绍了如何在 Spring Boot 应用中通过 @PropertySource 注解读取非默认配置文件,包括配置文件的创建、映射类的设计以及确保 Spring 容器能够正确加载这些配置的方法。 ... [详细]
  • This document outlines the recommended naming conventions for HTML attributes in Fast Components, focusing on readability and consistency with existing standards. ... [详细]
  • 网络运维工程师负责确保企业IT基础设施的稳定运行,保障业务连续性和数据安全。他们需要具备多种技能,包括搭建和维护网络环境、监控系统性能、处理突发事件等。本文将探讨网络运维工程师的职业前景及其平均薪酬水平。 ... [详细]
author-avatar
手机用户2502894533
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有