首页 > 学院 > 开发设计 > 正文

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

2019-11-06 09:44:53
字体:
来源:转载
供稿:网友
前言本篇将使用OkHttp实现文件的上传和下载,以及下载实现断点续传功能。因为是基本的使用,此系列文章主要是以大家会用为主而写的。当然,只要会用了,后面的优化、封装等等就不难了。下载使用OkHttp完成下载功能,实现断点续传,并附带进度条显示下载进度。文件下载的交互过程: 下载的流程: 文件下载的代码:public class DownloadActivity extends AppCompatActivity {    PRivate ProgressBar mProgressBar;    //准备下载    public static final int BEGIN = 0;    //正在下载    public static final int DOWNLOADING = 1;    //结束下载    public static final int END = 2;    //下载的进度    private static int progress;    //是否停止下载    private boolean cancel ;    OkHttpClient okHttpClient = new OkHttpClient();    MyHandler mHandler = new MyHandler(this);    private ImageView mShowImage;    ByteArrayOutputStream baos = new ByteArrayOutputStream();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_download);        mProgressBar = (ProgressBar) findViewById(R.id.down_progress_bar);        mShowImage = (ImageView) findViewById(R.id.down_image);    }    public void click2(View view) {        cancel = true;    }    public void click(View view) {        cancel = false;        new Thread(new Runnable() {            @Override            public void run() {                //实例化Builder对象                Request.Builder builder = new Request.Builder();                //设置Url                builder.url(Config.IMAGE_URL);                //获取已经下载的大小                int size = baos.size();                //size表示已经下载的大小。如果不为0,则进行断点续传。                if (size > 0) {                    //设置断点续传的开始位置,格式bytes=123456-                    builder.header("Range", "bytes=" + size + "-");                    //设置ProgressBar的当前进度从停止位置开始                    progress = size;                }                //创建Request对象                Request request = builder.build();                try {                    //执行下载请求,并获得Response对象                    Response response = okHttpClient.newCall(request).execute();                    //请求成功                    if (response.isSuccessful()) {                        //从Response对象中获取输入流对象                        InputStream inputStream = response.body().byteStream();                        //size==0表示第一次下载,非断点续传                        if (size == 0) {                            //获取文件的大小                            int contentLength = (int) response.body().contentLength();                            //将文件总大小通过Handler传递到UI线程,设置ProgressBar的总进度值                            mHandler.obtainMessage(BEGIN,contentLength,0).sendToTarget();                        }                        int len = 0;                        byte[] buffer = new byte[1024];                        //循环读取文件流,开始下载                        while((len = inputStream.read(buffer)) != -1) {                            if (cancel) {                                //如果点击了停止按钮,cancel为true。则结束循环                                break;                            }                            //将流写入缓存                            baos.write(buffer,0,len);                            baos.flush();                            //发送下载进度                            mHandler.obtainMessage(DOWNLOADING,len,0).sendToTarget();                        }                        //下载完成,结束请求,关闭body                        response.body().close();                        //将字节转成Bitmap对象                        byte[] bytes = baos.toByteArray();                        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);                        //下载完成通知更新试图                        mHandler.obtainMessage(END,bitmap).sendToTarget();                    }                } catch (IOException e) {                    e.printStackTrace();                }            }        }).start();    }    static class MyHandler extends Handler {        private WeakReference<DownloadActivity> activityWeakReference;        public MyHandler(DownloadActivity activity) {            this.activityWeakReference = new WeakReference<DownloadActivity>(activity);        }        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case BEGIN:                    activityWeakReference.get().mProgressBar.setMax(msg.arg1);                    break;                case DOWNLOADING:                    progress += msg.arg1;                    activityWeakReference.get().mProgressBar.setProgress(progress);                    break;                case END:                    progress = 0;                    activityWeakReference.get().mShowImage.setImageBitmap((Bitmap)msg.obj);                    break;            }        }    }}复制代码以上是文件下的代码。实现了断点续传,其中能进行断点续传的关键代码为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最后附上下载效果(注意点击按钮开始和停止后下载进度条的变化,实现断点续传)。 
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表