前言
OkHttp已经支持标准表单形式上传文件,具体方法参考OkHttp相关教程,本文实现文件上传的进度监听。
原理
关键点是自定义 支持进度反馈的RequestBody:
重写write方法按照自定义的SEGMENT_SIZE 来写文件,从而监听进度。
public class FileProgressRequestBody extends RequestBody {
public interface ProgressListener {
void transferred( long size );
}
public static final int SEGMENT_SIZE = 2*1024; // okio.Segment.SIZE
protected File file;
protected ProgressListener listener;
protected String contentType;
public FileProgressRequestBody(File file, String contentType, ProgressListener listener) {
this.file = file;
this.contentType = contentType;
this.listener = listener;
}
protected FileProgressRequestBody() {}
@Override
public long contentLength() {
return file.length();
}
@Override
public MediaType contentType() {
return MediaType.parse(contentType);
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(file);
long total = 0;
long read;
while ((read = source.read(sink.buffer(), SEGMENT_SIZE)) != -1) {
total += read;
sink.flush();
this.listener.transferred(total);
}
} finally {
Util.closeQuietly(source);
}
}
}
FileProgressRequestBody 以2KB为单位上传,对外暴露回调ProgressListener来发布进度。接着写一个上传管理类:HttpUploader,先构造Request对象:
protected Request generateRequest(String url){
// 构造上传请求,模拟表单提交文件
String formData = String.format("form-data;name=file; filename=%s", FileUtil.pickFileNameFromPath(fileInfo.filePath) );
FileProgressRequestBody filePart = new FileProgressRequestBody( new File(fileInfo.filePath) , "application/octet-stream" , this );
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addPart( Headers.of("Content-Disposition",formData), filePart )
.build();
// 创建Request对象
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
return request;
}
传入文件路径,formData是与服务端的header约定,此处约定:name是文件名称
定义文件上传的执行方法doUpload:
protected int doUpload(String url){
try {
OkHttpClient httpClient = OkHttpClientMgr.Instance().getOkHttpClient();
call = httpClient.newCall( generateRequest(url) );
Response response = call.execute();
if (response.isSuccessful()) {
sbFileUUID = new StringBuilder();
return readResponse(response,sbFileUUID);
} else( ... ) { // 重试
return STATUS_RETRY;
}
} catch (IOException ioe) {
LogUtil.e(LOG_TAG, "exception occurs while uploading file!",ioe);
}
return isCancelled() ? STATUS_CANCEL : STATUS_FAILED_EXIT;
}
上半部分是OkHttp请求的标准写法,然后对上传结果进行分析,和服务端约定返回结果。
里面的readResponse()方法就是每次上传后读取服务端的结果:
上传成功,可以让服务端返回文件的uuid,从response.body() 读取uuid
上传失败,和服务端约定一个状态码,比如500执行重试。
网友评论