安卓中带进度显示的图片上传

接上一篇:安卓选择图片上传功能
在这篇文章的基础上继续打造带进度的图片上传功能。过程蛮复杂,在本文中将贴出大量代码。嫌字多不看的出门左转。另外在上一篇文章中问我要demo的同学们,待后期我上传github后贴出地址。

前言

在查阅了大量资料以后,现在首先对于上传文件主要有两种方式。
- 把文件转换成字符串通过json格式上传【参考xutils3的post联网请求方式】
- 直接封装成一个File对象然后通过表单方式上传【参考okhttp上传文件方式】

首先第一种方式就是普通的联网请求,这种方式有人告诉我,如果是通过json格式去入参的话,无法拿到进度回调。因为json是拼接完成之后一次性全部发送。我不知道这种说法是否有依据来源。也就是说如果要实现进度,那么就要采用以文件形式上传,换而言之也就是流的概念。

一开始我采用的是HttpUrlConnection这种原始的联网工具,实际证明,这种方式只能拿到从堆内存中读到缓存中的进度,而不能拿到上传过程中的进度。在网上找寻许久,最后找到的方法综合起来,都是把okhttp的请求体进行封装来拿到进度。那么下面我们来看一下代码。

代码

1

其中最重要的文件是ProgressRequestBody


public class ProgressRequestBody extends RequestBody {

    private RequestBody mRequestBody;//真正的mRequestBody
    private OnProgressListener mProgressListener;//上传回调接口

    /**
     * 构造函数,赋值
     *
     * @param requestBody      待包装的请求体
     * @param progressListener 回调接口
     */
    public ProgressRequestBody(RequestBody requestBody, OnProgressListener progressListener) {
        mRequestBody = requestBody;
        mProgressListener = progressListener;
    }

    public interface OnProgressListener {
        void OnProgress(long byteWrite, long contentLength);
    }

    /**
     * 重写调用实际的响应体的contentType
     *
     * @return MediaType
     */
    @Override
    public MediaType contentType() {
        return mRequestBody.contentType();
    }

    @Override
    public long contentLength() {
        try {
            return mRequestBody.contentLength();//总字节长度
        } catch (IOException e) {
            return -1;
        }
    }

    /**
     * 重写进行写入
     *
     * @param sink BufferedSink
     * @throws IOException 异常
     */
    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        CountingSink countingSink = new CountingSink(sink);
        BufferedSink bufferedSink = Okio.buffer(countingSink);
        mRequestBody.writeTo(bufferedSink);
        bufferedSink.flush();
    }

    protected final class CountingSink extends ForwardingSink {
        private long byteWrite;//当前写入字节数

        public CountingSink(Sink delegate) {
            super(delegate);
        }

        @Override
        public void write(Buffer source, long byteCount) throws IOException {
            super.write(source, byteCount);
            byteWrite += byteCount;
            mProgressListener.OnProgress(byteWrite, contentLength());
        }
    }

这个重写的类看起来还是挺复杂的,简单解释一下,这个类是把okhttp中的RequestBody进行了封装,并且在里面设计了一个接口,在ForwardingSink这个抽象类里把进度暴露出来,这样的解释不知道有没有明白,如果不明白我还是建议去学习接口回调,这个东西的重要性不必多说。

然后在外部再封装一层,封装一层的原因是为了后期在这里做一些其他需求的操作。


public class CountingRequestBody extends ProgressRequestBody {

    /**
     * 再封装一层
     *
     * @param requestBody      请求体
     * @param progressListener 上传进度监听
     */
    public CountingRequestBody(RequestBody requestBody, OnProgressListener progressListener) {
        super(requestBody, progressListener);
    }

}

然后使用它


public class OkHttpUploadFileUtils {

    /**
     * 上传文件
     * 注意:这里的字段不能为null不然会报空指针
     *
     * @param file_path 文件路径
     * @param po        上传实体
     * @param callBack  外部回调
     */
    public static void uploadFile(String file_path, FilePo po, final UploadCallBack callBack) {
        File file = new File(file_path);
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("service", "--")
                .addFormDataPart("bucketName", po.getBucketName())
                .addFormDataPart("key", po.getKey())
                .addFormDataPart("system", po.getSystem())
                .addFormDataPart("file_name", po.getFile_name())
                .addFormDataPart("file_size", file.length() + "")
                .addFormDataPart("file_type", po.getFile_type())
                .addFormDataPart("operation", po.getOperation())
                .addFormDataPart("operator_id", po.getOperator_id())
                .addFormDataPart("operator_name", po.getOperator_name())
                .addFormDataPart("appkey", "--")
                .addFormDataPart("language", "--")
                .addFormDataPart("token", TpzIniUtils.getString("token_login", null))
                .addFormDataPart(
                        "inputStream",
                        file.getName(),
                        RequestBody.create(MediaType.parse("application/octet-stream"), file)
                )
                .build();
        CountingRequestBody countingRequestBody = new CountingRequestBody(requestBody, new CountingRequestBody.OnProgressListener() {
            @Override
            public void OnProgress(long byteWrite, long contentLength) {
                callBack.onUploading(byteWrite * 100 / contentLength);//暴露进度
            }
        });
        Request request = new Request.Builder()
                .url(UrlList.PREFIX_URL)    //URL
                .post(countingRequestBody)
                .build();
        execute(request, callBack);
    }

    //发起请求
    private static void execute(Request request, UploadCallBack callBack) {
        try {
            Call call = BaseApplication.okHttpClient.newCall(request);
            Response response = call.execute();//这里用的是同步回调
            JsonResults<FileUploadedBean> jsonResult = JsonTools.convertFromJson(
                    response.body().string(),
                    new TypeToken<JsonResults<FileUploadedBean>>() {
                    });
            if (jsonResult.getResult_data().isResult()) {
                callBack.onSuccess(jsonResult.getResult_data().isResult(), jsonResult.getResult_data().getFile_id());
            } else {
                callBack.onFailed(null);//失败回调
            }
        } catch (Exception e) {
            callBack.onFailed(e);//失败回调
        }
        callBack.onFinish();//结束回调
    }

}

这里有一些我项目中的东西不便于暴露,我想大多数中级水平的开发者都能看得懂。我自己设计了一个接口用于暴露数据,如下:


public interface UploadCallBack {

    void onSuccess(boolean result, String attach_id);

    void onFailed(Exception e);

    void onFinish();

    void onUploading(float progress);

}

这里使用的是一个同步的联网操作,如果有朋友有比较好的异步上传的逻辑欢迎留言评论。
然后在外部文件中调用:


OkHttpUploadFileUtils.uploadFile(imgList.get(i).getImg_url(), filePo, new UploadCallBack() {
                                    @Override
                                    public void onSuccess(boolean result, String attach_id) {
                                        imgList.get(finalI).setAttach_id(attach_id);
                                        setImgUploaderProgress(imgList.get(finalI), 100, result);
                                    }

                                    @Override
                                    public void onFailed(Exception e) {
                                        setImgUploaderProgress(imgList.get(finalI), 100, false);
                                    }

                                    @Override
                                    public void onFinish() {

                                    }

                                    @Override
                                    public void onUploading(float progress) {
                                        if (progress < 100) {
                                            setImgUploaderProgress(imgList.get(finalI), (int) progress, true);
                                        }
                                    }
                                });
                            }

这个方法可能部分人看不明白,这是我针对自己的逻辑做的一些UI处理,在onUploading这个方法中可以看到,进度从里面暴露出来。
实际效果:

2

后记

我师傅不让我使用一些第三方的工具,他说不能只做个搬运工,复制粘贴。有些东西还是要知道一下原理。进行二次封装,也避免了项目中引入大量依赖。尽可能的在能力之内维持项目的精简高效。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值