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

本文介绍如何使用Retrofit2与OkHttp3进行文件上传,并实现上传进度的监听功能。通过封装通用的Retrofit制造者及MultipartBody生成器,简化文件上传流程。同时扩展Request Body以支持进度回调。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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){
		
	}
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值