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

来源:互联网 发布:朋友圈图文制作软件 编辑:程序博客网 时间:2024/05/28 23:20

接上一篇:安卓选择图片上传功能
在这篇文章的基础上继续打造带进度的图片上传功能。过程蛮复杂,在本文中将贴出大量代码。嫌字多不看的出门左转。另外在上一篇文章中问我要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

后记

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

阅读全文
0 0