retrofit2+okhttp3图片上传及进度监听

1、使用jar包

由于retrofit2与1的写法有很大的变化,并且对okhttp存在着依赖。所以需要确定选择使用方向,以免陷入不必要的坑中。

本篇讲采用retrofit2 + okhttp3做结合使用。在导包过程中本人也遇到了一些不兼容的麻烦,再此做记录。

retrofit-2.1.0.jar、okhttp-3.4.1.jar、okio-1.6.0.jar、converter-gson-2.1.0.jar

另外我们服务端使用springmvc框架,通过@responseBody返回。前期引入gson包为gson-2.4.jar总报错不知何故,最后换成gson-2.7.jar就可以了。

2、使用retrofit

retrofit使用是有一定规律性,我们可以将其归纳后提出,封装一个针对retrofit的制造者。

public class RetrofitBuilder{
	private static Retrofit retrofit;
	
	public synchronized static Retrofit buildRetrofit(String url){
		if(retrofit == null){
			OkHttpClient client = new OkHttpClient
				.Builder()
				.retryOnConnectionFailure(true)
				.build();
				
			retrofit = new Retrofit
				.Builder
				.client(client)
				.baseUrl(url)
				.addConverterFactory((GsonConverterFactory.create()))
				.build();
		}
		
		return retrofit;
	}
}
retrofit对应请求接口

public interface FileuploadService{
	/**
	*@param parts
	*@param map(请求参数)
	*@return(JsonRetrun实体类)
	*/
	@Multipart
	@POST("uploader")
	Call<JsonReturn> uploadFilesWithParts(@Part() List<MultipartBody.Part> parts , @QueryMap Map<String , String> map);
	
	/**
	*@param body
	*@param map(请求参数)
	*@return(JsonRetrun实体类)
	*/
	@Multipart
	@POST("uploader")
	Call<JsonReturn> uploadFilesWithBody(@Body() MultipartBody body , @QueryMap Map<String , String> map);
}
回执泛型实体类
public class JsonReturn{
	private String errcode;
	private String errmsg;
	private String msg;
	private Object object;
	
	set/get方法.....
}
接口中Multipart参数生成,可以封装一个创造者。

public class MultipartBuiler{
	public static MultipartBody filesToBody(List<File> files , RetrofitCallback<JsonReturn> callback){
		for(File file : files){
			RequestBody requestBody = RequestBody.create(MediaType.parse("img/png"),file);
			FileRequestBody body = new FileRequestBody(reqeustBody , callback , file);
			builder.addFormDataPart("file" , file.getName , body);
		}
		
		builder.setType(MultipartBody.FORM);
		MultipartBody multipartBody = builder.build();
		return multipartBody;
	}

	public staitc List<MultipartBody.Part> filesToParts(List<File> files , RetrofitCallback<JsonReturn> callback){
		List<MultipartBody.Part> parts = new ArrayList<>(files.size());
		for(File file : files){
			RequestBody requestBody = RequestBody.create(MediaType.parse("img/png"),file);
			FileRequestBody body = new FileRequestBody(reqeustBody , callback , file);
			MultipartBody.Part part = MultipartBody.Part.createFormData("file" , file.getName() , body);
			parts.add(part);
		}

		return parts;
	}
}
以上可以实现文件的上传了。

3、有的时候我们在项目中需要监听文件的上传进度,那要如何实现呢。继续往下看思路。

上传的过程,我们可以考虑为request的交互过程,所以我们完全可以针对okhttp中的requestBody做些扩展。

/**
 * 扩展Okhttp的请求体, 实现上传时的进度监听
 * @author xuximing
 *
 * @param <T>
 */
public class FileRequestBody<T> extends RequestBody {
	/**
	 * 实体请求体
	 */
	private RequestBody requestBody;
	
	/**
	 * 上传回调接口
	 */
	private RetrofitCallback<T> callback;
	/**
	 * 文件
	 */
	private File file ;
	/**
	 * 包装完成的BufferedSink
	 */
	private BufferedSink bufferedSink;
	
	public FileRequestBody(RequestBody requestBody , RetrofitCallback<T> callback , File file){
		super();
		this.requestBody = requestBody;
		this.callback = callback;
		this.file = file;
	}
	
	
	@Override
	public long contentLength() throws IOException {
		return requestBody.contentLength();
	}



	@Override
	public MediaType contentType() {
		return requestBody.contentType();
	}

	@Override
	public void writeTo(BufferedSink sink) throws IOException {
		if(bufferedSink == null){
			//包装
			bufferedSink = Okio.buffer(sink(sink));
		}
		//写入
		requestBody.writeTo(bufferedSink);
		//必须调用flush,否则最后一部分数据可能不会被写入
		bufferedSink.flush();
	}

	/**
	 * 写入,回调进度接口
	 * @param sink
	 * @return
	 */
	private Sink sink(Sink sink){
		return new ForwardingSink(sink) {
			//当前写入字节数
			long bytesWritten = 0L;
			//总字节长度,避免多次调用contentLength()方法
			long contentLength = 0L;
			
			@Override
			public void write(Buffer source, long byteCount) throws IOException {
				super.write(source, byteCount);
				if(contentLength == 0){
					//获得contentLength的值,后续不再调用
					contentLength = contentLength();
				}
				//增加当前写入的字节数
				bytesWritten += byteCount;
				//回调
				callback.onLoading(file , contentLength, bytesWritten);
			}
		};
	}
}
retrofit中callback不能完全满足我们的需求,我们也可以进一步扩展

public abstract class RetrofitCallback<T> implements Callback<T>{

	@Override
	public void onResponse(Call<T> call, Response<T> response) {
		if(response.isSuccessful()){
			onSuccess(call, response);
		}else{
			onFailure(call, new Throwable(response.message()));
		}
		
	}

	public abstract void onSuccess(Call<T> call , Response<T> response);
	
	public void onLoading(File file , long total , long progress){
		
	}
}

4、最终使用

使用起来用到的代码依旧相同,那我们在次提取封装

public class UploadFileUitl{
	public UploadFileUitl(String url , Map<> paramMap , List mList , RetrofitCallBack callback){
		List<MultipartBody.Part> parts = MultipartBuilder.filesToParts(mList , callback);//创建Multipart
		//创建Retrofit,并调用enqueue实现异步操作
		RetrofitBuilder.buildRetrofit(url)
			       .create(FileUploadService.class)'
		       	       .uploadFilesWithParts(parts , paramMap)
                               .enqueue(callback);}
}

ok,开始最终的调用吧

new UploadFileUtil(url , paramMap , fileList , new RetrofitCallback(){
	//成功
	@Override
	public void onSuccess(Call call , Response response){}

	//失败
	@Override
	public void onFailure(Call call , Throwable t){}

	//加载中
	@Override
	public void onLoading(File file , long total , long progress){}
});

不要忘记android的规则,对一些耗时操作不要放在主线程中。因此我们还需要将最终调用放到子线程中。我用的是AsyncTask

class UploadTask extends AsyncTask<Map , Map , Object>{
	//在执行过程中调用上传监听过程
	@Override
	protected Object doInBackground(Map... params){
		new UploadFileUtil(url , paramMap , fileList , new RetrofitCallback(){
			//成功
			@Override
			public void onSuccess(Call call , Response response){}

			//失败
			@Override
			public void onFailure(Call call , Throwable t){}

			//加载中
			@Override
			public void onLoading(File file , long total , long progress){
				....
				publishProgress(map);//出发处理进度
			}
		});
	}

	//处理进度
	@Override
	protected void onProgressUpdate(Map... maps){
		
	}
}




 


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用RxJava2 + Retrofit2 + OKHttp进行POST请求,可以按照以下步骤进行: 1. 添加依赖 在项目的build.gradle文件中添加以下依赖: ``` dependencies { implementation 'io.reactivex.rxjava2:rxjava:2.2.19' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.9.1' } ``` 2. 创建Service接口 创建一个接口,用于定义POST请求的方法。例如: ``` public interface ApiService { @POST("login") Observable<LoginResponse> login(@Body LoginRequest request); } ``` 3. 创建Retrofit对象 在Application类或其他初始化类中,创建Retrofit对象: ``` public class MyApp extends Application { private static ApiService apiService; @Override public void onCreate() { super.onCreate(); // 创建OkHttpClient对象 OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build(); // 创建Retrofit对象 Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://example.com/api/") .client(client) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); apiService = retrofit.create(ApiService.class); } public static ApiService getApiService() { return apiService; } } ``` 4. 发起POST请求 在需要发起POST请求的地方,可以使用以下代码: ``` LoginRequest request = new LoginRequest(); request.setUsername("admin"); request.setPassword("123456"); MyApp.getApiService().login(request) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<LoginResponse>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(LoginResponse response) { // 处理响应数据 } @Override public void onError(Throwable e) { // 处理异常 } @Override public void onComplete() { } }); ``` 上述代码中,我们首先创建了一个LoginRequest对象,用于存储要发送的数据。然后调用MyApp.getApiService().login(request)方法,发起POST请求。在这里,我们使用了RxJava2的Observable对象,将请求结果封装为一个可观察对象。使用subscribeOn(Schedulers.io())指定在IO线程中进行网络请求,使用observeOn(AndroidSchedulers.mainThread())指定在主线程中处理响应。最后通过subscribe方法订阅请求结果,处理响应数据或异常。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值