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

  • 前言

本篇将使用OkHttp实现文件的上传和下载,以及下载实现断点续传功能。因为是基本的使用,此系列文章主要是以大家会用为主而写的。当然,只要会用了,后面的优化、封装等等就不难了。

 

  • 下载

使用OkHttp完成下载功能,实现断点续传,并附带进度条显示下载进度。
文件下载的交互过程:


 
下载的流程:

 

 

 

  • 文件下载的代码:

 

  1. public class DownloadActivity extends AppCompatActivity {
  2.  
  3.     private ProgressBar mProgressBar;
  4.     //准备下载
  5.     public static final int BEGIN = 0;
  6.     //正在下载
  7.     public static final int DOWNLOADING = 1;
  8.     //结束下载
  9.     public static final int END = 2;
  10.     //下载的进度
  11.     private static int progress;
  12.     //是否停止下载
  13.     private boolean cancel ;
  14.  
  15.     OkHttpClient okHttpClient = new OkHttpClient();
  16.     MyHandler mHandler = new MyHandler(this);
  17.     private ImageView mShowImage;
  18.     ByteArrayOutputStream baos = new ByteArrayOutputStream();
  19.  
  20.     @Override
  21.     protected void onCreate(Bundle savedInstanceState) {
  22.         super.onCreate(savedInstanceState);
  23.         setContentView(R.layout.activity_download);
  24.         mProgressBar = (ProgressBar) findViewById(R.id.down_progress_bar);
  25.         mShowImage = (ImageView) findViewById(R.id.down_image);
  26.     }
  27.  
  28.     public void click2(View view) {
  29.         cancel = true;
  30.     }
  31.  
  32.     public void click(View view) {
  33.  
  34.         cancel = false;
  35.         new Thread(new Runnable() {
  36.             @Override
  37.             public void run() {
  38.                 //实例化Builder对象
  39.                 Request.Builder builder = new Request.Builder();
  40.                 //设置Url
  41.                 builder.url(Config.IMAGE_URL);
  42.                 //获取已经下载的大小
  43.                 int size = baos.size();
  44.                 //size表示已经下载的大小。如果不为0,则进行断点续传。
  45.                 if (size > 0) {
  46.                     //设置断点续传的开始位置,格式bytes=123456-
  47.                     builder.header("Range", "bytes=" + size + "-");
  48.                     //设置ProgressBar的当前进度从停止位置开始
  49.                     progress = size;
  50.                 }
  51.                 //创建Request对象
  52.                 Request request = builder.build();
  53.                 try {
  54.                     //执行下载请求,并获得Response对象
  55.                     Response response = okHttpClient.newCall(request).execute();
  56.                     //请求成功
  57.                     if (response.isSuccessful()) {
  58.                         //从Response对象中获取输入流对象
  59.                         InputStream inputStream = response.body().byteStream();
  60.                         //size==0表示第一次下载,非断点续传
  61.                         if (size == 0) {
  62.                             //获取文件的大小
  63.                             int contentLength = (int) response.body().contentLength();
  64.                             //将文件总大小通过Handler传递到UI线程,设置ProgressBar的总进度值
  65.                             mHandler.obtainMessage(BEGIN,contentLength,0).sendToTarget();
  66.                         }
  67.  
  68.                         int len = 0;
  69.                         byte[] buffer = new byte[1024];
  70.                         //循环读取文件流,开始下载
  71.                         while((len = inputStream.read(buffer)) != -1) {
  72.                             if (cancel) {
  73.                                 //如果点击了停止按钮,cancel为true。则结束循环
  74.                                 break;
  75.                             }
  76.                             //将流写入缓存
  77.                             baos.write(buffer,0,len);
  78.                             baos.flush();
  79.                             //发送下载进度
  80.                             mHandler.obtainMessage(DOWNLOADING,len,0).sendToTarget();
  81.                         }
  82.                         //下载完成,结束请求,关闭body
  83.                         response.body().close();
  84.  
  85.                         //将字节转成Bitmap对象
  86.                         byte[] bytes = baos.toByteArray();
  87.                         Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
  88.  
  89.                         //下载完成通知更新试图
  90.                         mHandler.obtainMessage(END,bitmap).sendToTarget();
  91.                     }
  92.                 } catch (IOException e) {
  93.                     e.printStackTrace();
  94.                 }
  95.             }
  96.         }).start();
  97.     }
  98.  
  99.     static class MyHandler extends Handler {
  100.         private WeakReference activityWeakReference;
  101.  
  102.         public MyHandler(DownloadActivity activity) {
  103.             this.activityWeakReference = new WeakReference(activity);
  104.         }
  105.  
  106.         @Override
  107.         public void handleMessage(Message msg) {
  108.             switch (msg.what) {
  109.                 case BEGIN:
  110.                     activityWeakReference.get().mProgressBar.setMax(msg.arg1);
  111.                     break;
  112.                 case DOWNLOADING:
  113.                     progress += msg.arg1;
  114.                     activityWeakReference.get().mProgressBar.setProgress(progress);
  115.                     break;
  116.                 case END:
  117.                     progress = 0;
  118.                     activityWeakReference.get().mShowImage.setImageBitmap((Bitmap)msg.obj);
  119.                     break;
  120.             }
  121.         }
  122.     }
  123.  
  124. }

复制代码

以上是文件下的代码。实现了断点续传,其中能进行断点续传的关键代码为builder.header("Range", "bytes=" + size + "-");代码中都有注释,结合上面的流程图应该不难理解。
代码解释:源码的效果是点击开始按钮出发click事件开始下载,点击停止按钮触发click2事件中断下载。下载的进度监听是在while循环中,通过Handler进行的进度更新。

 

  • 文件上传

本案例中,实现带参数的文件上传功能-----同时完成参数传递和文件上传。
代码如下:

 

 

public class UploadActivity extends AppCompatActivity {

 

    public static final int GET_PIC = 1;

    private ImageView mShowImage;

    private Uri uri;

    OkHttpClient okHttpClient = new OkHttpClient();

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_upload);

        mShowImage = (ImageView) findViewById(R.id.upload_show_image);

    }

 

    public void click(View view) {

        switch (view.getId()) {

            case R.id.upload_choose_file:

                choosePic();//选择图片

                break;

            case R.id.upload_start:

                upload();//上传

                break;

        }

    }

 

    /**

     * 上传

     */

    private void upload() {

        if (uri == null) {

            Toast.makeText(UploadActivity.this, "请先选择文件", Toast.LENGTH_SHORT).show();

            return;

        }

        //设置文件的媒体类型,image/*表示匹配所有的图片文件

        MediaType mediaType = MediaType.parse("image/*");

        MultipartBody.Builder builder = new MultipartBody.Builder();

        //文件上传,此处是关键,设置媒体类型为multipart/form-data,表示多种格式的表单数据上传

      builder.setType(MultipartBody.FORM);

        //添加上传的参数username=androidxx

        builder.addFormDataPart("username","androidxx");

        //添加上传的文件。文件是从相册读取的文件流。

        try {

            //获得需要上传的文件流

            InputStream inputStream = getContentResolver().openInputStream(uri);

            int len = 0;

            byte[] buffer = new byte[1024];

            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            while((len = inputStream.read(buffer)) != -1) {

                baos.write(buffer, 0, len);

            }

            inputStream.close();

            /*

             * 添加文件到Builder中。如果要实现多文件同时上传,可以添加多个addFormDataPart。

             * 注意:

             * 参数一:上传的文件的标示,同username。也就是可以在服务器端通过upload找到对应的文件流

             * 参数二:文件的名称。上传到服务器之后以此名称命名文件

             * 参数三:需要上传的文件。包含在RequestBody中

             * RequestBody.create方法有多个重载的方法,可以选择不同的数据源。此处选择的是字节形式(baos.toByteArray())的数据眼。

             */

builder.addFormDataPart("upload", "test.jpg", RequestBody.create(mediaType, baos.toByteArray()));

        } catch (IOException e) {

            e.printStackTrace();

        }

        //创建MultipartBody对象,MultipartBody是RequestBody的子类,用于文件上传。

        MultipartBody multipartBody = builder.build();

        Request request = new Request.Builder()

                .url("http://192.168.3.4:8080/WebServer/upload.do")//上传的服务器地址

                .post(multipartBody)

                .build();

        //开始上传。采用Post异步请求的方式

        okHttpClient.newCall(request).enqueue(new Callback() {

            @Override

            public void onFailure(Call call, IOException e) {

                Log.d("androidxx.cn","--" + e.getMessage());

                e.printStackTrace();

            }

 

            @Override

            public void onResponse(Call call, Response response) throws IOException {

                //接受到成功的返回结果

                if (response.isSuccessful()) {

                    Log.d("androidxx.cn","-上传成功-");

                } else {

                    Log.d("androidxx.cn","-失败--" + response.body().string());

                }

 

            }

        });

    }

 

    /**

     * 打开相册,选择文件后返回

     */

    private void choosePic() {

        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

        intent.setType("image/*");

        startActivityForResult(intent,GET_PIC);

 

    }

 

    /**

     * 接收选择的图片

     * @param requestCode

     * @param resultCode

     * @param data

     */

    @Override

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (resultCode == RESULT_CANCELED) {

            return;

        }

        //获得图片的URI

        uri = data.getData();

        //通过ContentResolver获得图片对象

        ContentResolver contentResolver = getContentResolver();

        InputStream inputStream = null;

        try {

            inputStream = contentResolver.openInputStream(uri);

        } catch (FileNotFoundException e) {

            e.printStackTrace();

        }

        //将流转换成图片,显示到ImageView中

        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

        mShowImage.setImageBitmap(bitmap);

    }

}

复制代码如上代码,第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
最后附上下载效果(注意点击按钮开始和停止后下载进度条的变化,实现断点续传)。 

 

 

你可能感兴趣的:(android进阶)