Retrofit2 & RxJava2实现单文件和多文件上传

// 拦截okHttp的日志,如果开启了会导致上传回调被调用两次

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();

interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

builder.addInterceptor(interceptor);

}

// 超时时间

builder.connectTimeout(15, TimeUnit.SECONDS);// 15S连接超时

builder.readTimeout(20, TimeUnit.SECONDS);// 20s读取超时

builder.writeTimeout(20, TimeUnit.SECONDS);// 20s写入超时

// 错误重连

builder.retryOnConnectionFailure(true);

okHttpClient = builder.build();

}

}

}

return okHttpClient;

}

}

这个类主要是获取OkHttpClient示例,设置它的一些参数,比如超时时间,拦截器等等.

封装RetrofitClient类


/**

  • RetrofitClient.

  • @author devilwwj

  • @since 2017/7/12

*/

public class RetrofitClient {

private static RetrofitClient mInstance;

private static Retrofit retrofit;

private RetrofitClient() {

retrofit = RetrofitBuilder.buildRetrofit();

}

/**

  • 获取RetrofitClient实例.

  • @return 返回RetrofitClient单例

*/

public static synchronized RetrofitClient getInstance() {

if (mInstance == null) {

mInstance = new RetrofitClient();

}

return mInstance;

}

private T create(Class clz) {

return retrofit.create(clz);

}

/**

  • 单上传文件的封装.

  • @param url 完整的接口地址

  • @param file 需要上传的文件

  • @param fileUploadObserver 上传回调

*/

public void upLoadFile(String url, File file,

FileUploadObserver fileUploadObserver) {

UploadFileRequestBody uploadFileRequestBody =

new UploadFileRequestBody(file, fileUploadObserver);

create(UploadFileApi.class)

.uploadFile(url, MultipartBuilder.fileToMultipartBody(file,

uploadFileRequestBody))

.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())

.subscribe(fileUploadObserver);

}

/**

  • 多文件上传.

  • @param url 上传接口地址

  • @param files 文件列表

  • @param fileUploadObserver 文件上传回调

*/

public void upLoadFiles(String url, List files,

FileUploadObserver fileUploadObserver) {

create(UploadFileApi.class)

.uploadFile(url, MultipartBuilder.filesToMultipartBody(files,

fileUploadObserver))

.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())

.subscribe(fileUploadObserver);

}

}

这个是Retrofit客户端类,获取它的单例然后去调用它的上传文件的方法,可以看到我这里封装了两个方法,uploadFile是上传单个文件,uploadFiles方法上传多个文件.

因为我们需要构造一个Retrofit对象,所以这里有一个RetrofitBuilder类:

/**

  • Retrofit构造器.

  • @author devilwwj

  • @since 2017/7/13

*/

public class RetrofitBuilder {

private static Retrofit retrofit;

public static synchronized Retrofit buildRetrofit() {

if (retrofit == null) {

Gson gson = new GsonBuilder().setDateFormat(“yyyy-MM-dd HH:mm:ss”).create();

GsonConverterFactory gsonConverterFactory = GsonConverterFactory.create(gson);

retrofit = new Retrofit.Builder().client(OkHttpManager.getInstance())

.baseUrl(AppConfig.HTTP_SERVER)

.addConverterFactory(gsonConverterFactory)

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

.build();

}

return retrofit;

}

}

可以看到,想构造Retrofit对象是需要获取OkhttpClient实例的。

定义上传文件接口


/**

  • 上传API.

  • @author devilwwj

  • @since 2017/7/12

*/

public interface UploadFileApi {

String UPLOAD_FILE_URL = AppConfig.HTTP_SERVER + “file/upload”;

@POST

Observable uploadFile(@Url String url, @Body MultipartBody body);

}

这里就是Retrofit定义接口的形式,通过注解来表示各个参数,@POST表示发起post请求,@Url表示这是个请求地址,@Body表示这是请求体,关于Retrofit的各种注解的使用这里不多说,大家可以自行了解。

构造MultipartBody


上一步定义好了上传的接口,我们最终是要去构造MultipartBody,这一块就需要跟后台同学进行沟通了,根据接口定义来实现,这里是我们的实现:

/**

  • MultipartBuilder.

  • @author devilwwj

  • @since 2017/7/13

*/

public class MultipartBuilder {

/**

  • 单文件上传构造.

  • @param file 文件

  • @param requestBody 请求体

  • @return MultipartBody

*/

public static MultipartBody fileToMultipartBody(File file, RequestBody requestBody) {

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

JsonObject jsonObject = new JsonObject();

jsonObject.addProperty(“fileName”, file.getName());

jsonObject.addProperty(“fileSha”, Utils.getFileSha1(file));

jsonObject.addProperty(“appId”, “test0002”);

builder.addFormDataPart(“file”, file.getName(), requestBody);

builder.addFormDataPart(“params”, jsonObject.toString());

builder.setType(MultipartBody.FORM);

return builder.build();

}

/**

  • 多文件上传构造.

  • @param files 文件列表

  • @param fileUploadObserver 文件上传回调

  • @return MultipartBody

*/

public static MultipartBody filesToMultipartBody(List files,

FileUploadObserver fileUploadObserver) {

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

JsonArray jsonArray = new JsonArray();

Gson gson = new Gson();

for (File file : files) {

UploadFileRequestBody uploadFileRequestBody =

new UploadFileRequestBody(file, fileUploadObserver);

JsonObject jsonObject = new JsonObject();

jsonObject.addProperty(“fileName”, file.getName());

jsonObject.addProperty(“fileSha”, Utils.getFileSha1(file));

jsonObject.addProperty(“appId”, “test0002”);

jsonArray.add(jsonObject);

LogUtil.d(jsonObject.toString());

builder.addFormDataPart(“file”, file.getName(), uploadFileRequestBody);

}

builder.addFormDataPart(“params”, gson.toJson(jsonArray));

LogUtil.d(gson.toJson(jsonArray));

builder.setType(MultipartBody.FORM);

return builder.build();

}

}

自定义RequestBody


构造MultipartBody是需要去创建每个文件对应的ReqeustBody,但我们这边需要监听到文件上传成功、失败和进度的状态,所以需要去自定义:

/**

  • 上传文件请求body.

  • @author devilwwj

  • @since 2017/7/12

*/

public class UploadFileRequestBody extends RequestBody {

private RequestBody mRequestBody;

private FileUploadObserver fileUploadObserver;

public UploadFileRequestBody(File file, FileUploadObserver fileUploadObserver) {

this.mRequestBody = RequestBody.create(MediaType.parse(“application/octet-stream”), file);

this.fileUploadObserver = fileUploadObserver;

}

@Override

public MediaType contentType() {

return mRequestBody.contentType();

}

@Override

public long contentLength() throws IOException {

return mRequestBody.contentLength();

}

@Override

public void writeTo(BufferedSink sink) throws IOException {

CountingSink countingSink = new CountingSink(sink);

BufferedSink bufferedSink = Okio.buffer(countingSink);

// 写入

mRequestBody.writeTo(bufferedSink);

// 刷新

// 必须调用flush,否则最后一部分数据可能不会被写入

bufferedSink.flush();

}

/**

  • CountingSink.

*/

protected final class CountingSink extends ForwardingSink {

private long bytesWritten = 0;

public CountingSink(Sink delegate) {

super(delegate);

}

@Override

public void write(Buffer source, long byteCount) throws IOException {

super.write(source, byteCount);

bytesWritten += byteCount;

if (fileUploadObserver != null) {

fileUploadObserver.onProgressChange(bytesWritten, contentLength());

}

}

}

}

这里有个RxJava2的Observer的抽象类,主要是用来收到Rxjava2的事件:

/**

  • 上传文件的RxJava2回调.

  • @author devilwwj

  • @since 2017/7/12

  • @param 模板类

*/

public abstract class FileUploadObserver extends DefaultObserver {

@Override

public void onNext(T t) {

onUploadSuccess(t);

}

@Override

public void onError(Throwable e) {

onUploadFail(e);

}

@Override

public void onComplete() {

}

// 上传成功的回调

public abstract void onUploadSuccess(T t);

// 上传失败回调

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-cb9hoNZ9-1715763629835)]

[外链图片转存中…(img-4wfV82z5-1715763629837)]

[外链图片转存中…(img-XUN0ok4p-1715763629838)]

[外链图片转存中…(img-A0GgGQLy-1715763629839)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android中,获取Uri真实路径转换File对象可以使用以下代码: ```java public static File getFileFromUri(Context context, Uri uri) { String filePath = null; if (DocumentsContract.isDocumentUri(context, uri)) { // 如果是Document类型的Uri,则通过Document ID来进行解析 String documentId = DocumentsContract.getDocumentId(uri); if ("com.android.providers.media.documents".equals(uri.getAuthority())) { // MediaProvider的Uri String[] split = documentId.split(":"); String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } String selection = MediaStore.Images.Media._ID + "=?"; String[] selectionArgs = new String[]{split[1]}; filePath = getDataColumn(context, contentUri, selection, selectionArgs); } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { // DownloadsProvider的Uri Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId)); filePath = getDataColumn(context, contentUri, null, null); } else if ("com.android.externalstorage.documents".equals(uri.getAuthority())) { // ExternalStorageProvider的Uri String[] split = documentId.split(":"); if ("primary".equalsIgnoreCase(split[0])) { filePath = Environment.getExternalStorageDirectory() + "/" + split[1]; } } } else if ("content".equalsIgnoreCase(uri.getScheme())) { // 如果是普通Content类型的Uri,则直接查询该Uri对应的数据 filePath = getDataColumn(context, uri, null, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { // 如果是File类型的Uri,则直接获取File路径 filePath = uri.getPath(); } return new File(filePath); } private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { String[] projection = {MediaStore.Images.Media.DATA}; Cursor cursor = null; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { int columnIndex = cursor.getColumnIndexOrThrow(projection[0]); return cursor.getString(columnIndex); } } finally { if (cursor != null) { cursor.close(); } } return null; } ``` 其中,getDataColumn方法用于查询指定Uri对应的文件路径,getFileFromUri方法则将Uri转换File对象。需要注意的是,不同类型的Uri需要进行不同的解析方式,具体可以根据代码注释进行理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值