Javen天道酬勤

努力良心自成就

OkHttp基本使用(三)上传下载功能实现

  • 前言
本篇将使用OkHttp实现文件的上传和下载,以及下载实现断点续传功能。因为是基本的使用,此系列文章主要是以大家会用为主而写的。当然,只要会用了,后面的优化、封装等等就不难了。
  • 下载
使用OkHttp完成下载功能,实现断点续传,并附带进度条显示下载进度。
文件下载的交互过程:
 
下载的流程:
 
  • 文件下载的代码:
  1. public class DownloadActivity extends AppCompatActivity {

  2.     private ProgressBar mProgressBar;
  3.     //准备下载
  4.     public static final int BEGIN = 0;
  5.     //正在下载
  6.     public static final int DOWNLOADING = 1;
  7.     //结束下载
  8.     public static final int END = 2;
  9.     //下载的进度
  10.     private static int progress;
  11.     //是否停止下载
  12.     private boolean cancel ;

  13.     OkHttpClient okHttpClient = new OkHttpClient();
  14.     MyHandler mHandler = new MyHandler(this);
  15.     private ImageView mShowImage;
  16.     ByteArrayOutputStream baos = new ByteArrayOutputStream();

  17.     @Override
  18.     protected void onCreate(Bundle savedInstanceState) {
  19.         super.onCreate(savedInstanceState);
  20.         setContentView(R.layout.activity_download);
  21.         mProgressBar = (ProgressBar) findViewById(R.id.down_progress_bar);
  22.         mShowImage = (ImageView) findViewById(R.id.down_image);
  23.     }

  24.     public void click2(View view) {
  25.         cancel = true;
  26.     }

  27.     public void click(View view) {

  28.         cancel = false;
  29.         new Thread(new Runnable() {
  30.             @Override
  31.             public void run() {
  32.                 //实例化Builder对象
  33.                 Request.Builder builder = new Request.Builder();
  34.                 //设置Url
  35.                 builder.url(Config.IMAGE_URL);
  36.                 //获取已经下载的大小
  37.                 int size = baos.size();
  38.                 //size表示已经下载的大小。如果不为0,则进行断点续传。
  39.                 if (size > 0) {
  40.                     //设置断点续传的开始位置,格式bytes=123456-
  41.                     builder.header("Range", "bytes=" + size + "-");
  42.                     //设置ProgressBar的当前进度从停止位置开始
  43.                     progress = size;
  44.                 }
  45.                 //创建Request对象
  46.                 Request request = builder.build();
  47.                 try {
  48.                     //执行下载请求,并获得Response对象
  49.                     Response response = okHttpClient.newCall(request).execute();
  50.                     //请求成功
  51.                     if (response.isSuccessful()) {
  52.                         //从Response对象中获取输入流对象
  53.                         InputStream inputStream = response.body().byteStream();
  54.                         //size==0表示第一次下载,非断点续传
  55.                         if (size == 0) {
  56.                             //获取文件的大小
  57.                             int contentLength = (int) response.body().contentLength();
  58.                             //将文件总大小通过Handler传递到UI线程,设置ProgressBar的总进度值
  59.                             mHandler.obtainMessage(BEGIN,contentLength,0).sendToTarget();
  60.                         }

  61.                         int len = 0;
  62.                         byte[] buffer = new byte[1024];
  63.                         //循环读取文件流,开始下载
  64.                         while((len = inputStream.read(buffer)) != -1) {
  65.                             if (cancel) {
  66.                                 //如果点击了停止按钮,cancel为true。则结束循环
  67.                                 break;
  68.                             }
  69.                             //将流写入缓存
  70.                             baos.write(buffer,0,len);
  71.                             baos.flush();
  72.                             //发送下载进度
  73.                             mHandler.obtainMessage(DOWNLOADING,len,0).sendToTarget();
  74.                         }
  75.                         //下载完成,结束请求,关闭body
  76.                         response.body().close();

  77.                         //将字节转成Bitmap对象
  78.                         byte[] bytes = baos.toByteArray();
  79.                         Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

  80.                         //下载完成通知更新试图
  81.                         mHandler.obtainMessage(END,bitmap).sendToTarget();
  82.                     }
  83.                 } catch (IOException e) {
  84.                     e.printStackTrace();
  85.                 }
  86.             }
  87.         }).start();
  88.     }

  89.     static class MyHandler extends Handler {
  90.         private WeakReference<DownloadActivity> activityWeakReference;

  91.         public MyHandler(DownloadActivity activity) {
  92.             this.activityWeakReference = new WeakReference<DownloadActivity>(activity);
  93.         }

  94.         @Override
  95.         public void handleMessage(Message msg) {
  96.             switch (msg.what) {
  97.                 case BEGIN:
  98.                     activityWeakReference.get().mProgressBar.setMax(msg.arg1);
  99.                     break;
  100.                 case DOWNLOADING:
  101.                     progress += msg.arg1;
  102.                     activityWeakReference.get().mProgressBar.setProgress(progress);
  103.                     break;
  104.                 case END:
  105.                     progress = 0;
  106.                     activityWeakReference.get().mShowImage.setImageBitmap((Bitmap)msg.obj);
  107.                     break;
  108.             }
  109.         }
  110.     }

  111. }
复制代码
以上是文件下的代码。实现了断点续传,其中能进行断点续传的关键代码为builder.header("Range", "bytes=" + size + "-");代码中都有注释,结合上面的流程图应该不难理解。
代码解释:源码的效果是点击开始按钮出发click事件开始下载,点击停止按钮触发click2事件中断下载。下载的进度监听是在while循环中,通过Handler进行的进度更新。
  • 文件上传
本案例中,实现带参数的文件上传功能-----同时完成参数传递和文件上传。
代码如下:
  1. public class UploadActivity extends AppCompatActivity {

  2.     public static final int GET_PIC = 1;
  3.     private ImageView mShowImage;
  4.     private Uri uri;
  5.     OkHttpClient okHttpClient = new OkHttpClient();
  6.     @Override
  7.     protected void onCreate(Bundle savedInstanceState) {
  8.         super.onCreate(savedInstanceState);
  9.         setContentView(R.layout.activity_upload);
  10.         mShowImage = (ImageView) findViewById(R.id.upload_show_image);
  11.     }

  12.     public void click(View view) {
  13.         switch (view.getId()) {
  14.             case R.id.upload_choose_file:
  15.                 choosePic();//选择图片
  16.                 break;
  17.             case R.id.upload_start:
  18.                 upload();//上传
  19.                 break;
  20.         }
  21.     }

  22.     /**
  23.      * 上传
  24.      */
  25.     private void upload() {
  26.         if (uri == null) {
  27.             Toast.makeText(UploadActivity.this, "请先选择文件", Toast.LENGTH_SHORT).show();
  28.             return;
  29.         }
  30.         //设置文件的媒体类型,image/*表示匹配所有的图片文件
  31.         MediaType mediaType = MediaType.parse("image/*");
  32.         MultipartBody.Builder builder = new MultipartBody.Builder();
  33.         //文件上传,此处是关键,设置媒体类型为multipart/form-data,表示多种格式的表单数据上传
  34.       builder.setType(MultipartBody.FORM);
  35.         //添加上传的参数username=androidxx
  36.         builder.addFormDataPart("username","androidxx");
  37.         //添加上传的文件。文件是从相册读取的文件流。
  38.         try {
  39.             //获得需要上传的文件流
  40.             InputStream inputStream = getContentResolver().openInputStream(uri);
  41.             int len = 0;
  42.             byte[] buffer = new byte[1024];
  43.             ByteArrayOutputStream baos = new ByteArrayOutputStream();
  44.             while((len = inputStream.read(buffer)) != -1) {
  45.                 baos.write(buffer, 0, len);
  46.             }
  47.             inputStream.close();
  48.             /*
  49.              * 添加文件到Builder中。如果要实现多文件同时上传,可以添加多个addFormDataPart。
  50.              * 注意:
  51.              * 参数一:上传的文件的标示,同username。也就是可以在服务器端通过upload找到对应的文件流
  52.              * 参数二:文件的名称。上传到服务器之后以此名称命名文件
  53.              * 参数三:需要上传的文件。包含在RequestBody中
  54.              * RequestBody.create方法有多个重载的方法,可以选择不同的数据源。此处选择的是字节形式(baos.toByteArray())的数据眼。
  55.              */
  56. builder.addFormDataPart("upload", "test.jpg", RequestBody.create(mediaType, baos.toByteArray()));
  57.         } catch (IOException e) {
  58.             e.printStackTrace();
  59.         }
  60.         //创建MultipartBody对象,MultipartBody是RequestBody的子类,用于文件上传。
  61.         MultipartBody multipartBody = builder.build();
  62.         Request request = new Request.Builder()
  63.                 .url("http://192.168.3.4:8080/WebServer/upload.do")//上传的服务器地址
  64.                 .post(multipartBody)
  65.                 .build();
  66.         //开始上传。采用Post异步请求的方式
  67.         okHttpClient.newCall(request).enqueue(new Callback() {
  68.             @Override
  69.             public void onFailure(Call call, IOException e) {
  70.                 Log.d("androidxx.cn","--" + e.getMessage());
  71.                 e.printStackTrace();
  72.             }

  73.             @Override
  74.             public void onResponse(Call call, Response response) throws IOException {
  75.                 //接受到成功的返回结果
  76.                 if (response.isSuccessful()) {
  77.                     Log.d("androidxx.cn","-上传成功-");
  78.                 } else {
  79.                     Log.d("androidxx.cn","-失败--" + response.body().string());
  80.                 }

  81.             }
  82.         });
  83.     }

  84.     /**
  85.      * 打开相册,选择文件后返回
  86.      */
  87.     private void choosePic() {
  88.         Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
  89.         intent.setType("image/*");
  90.         startActivityForResult(intent,GET_PIC);

  91.     }

  92.     /**
  93.      * 接收选择的图片
  94.      * @param requestCode
  95.      * @param resultCode
  96.      * @param data
  97.      */
  98.     @Override
  99.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  100.         if (resultCode == RESULT_CANCELED) {
  101.             return;
  102.         }
  103.         //获得图片的URI
  104.         uri = data.getData();
  105.         //通过ContentResolver获得图片对象
  106.         ContentResolver contentResolver = getContentResolver();
  107.         InputStream inputStream = null;
  108.         try {
  109.             inputStream = contentResolver.openInputStream(uri);
  110.         } catch (FileNotFoundException e) {
  111.             e.printStackTrace();
  112.         }
  113.         //将流转换成图片,显示到ImageView中
  114.         Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
  115.         mShowImage.setImageBitmap(bitmap);
  116.     }
  117. }
复制代码
如上代码,第37行和第59行是上传能成功的重点。
  • 多文件长传
实现多文件上传,只需将上面单文件上传中的builder.addFormDataPart("upload", "test.jpg", RequestBody.create(mediaType, baos.toByteArray()));这一句执行多次,即使用多个addFormDataPart方法添加多个文件,然后就可以同时上传多个文件了。


  • 总结
1、文件上传和下载的过程其实就是一种特殊的Post和Get请求。总体的过程与Post请求和Get请求方式一样。
2、下载相当于一个特殊的Get请求,只是服务器返回的数据格式是文件流。我们也只能通过读取流来获得数据。
3、上传相当于一个特殊的Post请求,前面我们说过,Post请求就是传参数比较特殊和多样化。文件上传就是一种特殊的参数传递----参数是一个文件。
大家在看如上代码的时候,不要觉得陌生,其实代码的流程和逻辑同Post和Get请求一样,只是多了几行代码。

本案例的android端源码:Github
本案例的上传服务器配置方式【点击查看】,上传服务端源码:Github     服务器端代码请阅读Github中的readme.md文件。服务器端代码导入工程没有错之后,可以将代码加载到服务器,之后启动服务器就可以运行。本案例使用的是Tomcat服务器。
备注:对于android程序员如果想运行服务器代码,按照readme.md文档。然后有不懂的,可以留言。
转载请注明:androidxx.cn
最后附上下载效果(注意点击按钮开始和停止后下载进度条的变化,实现断点续传)。 
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/muyi_amen/article/details/58586677
文章标签: android okhttp
个人分类: android进阶
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭