1.文件上传
主要使用RequestBody
和MultipartBody.Builder
两个对象
1.1 上传一个40M视频
OkHttp
版本为 3.5
Activity代码:
上传主要用到一个MultipartBody.Builder
来添加文件,addFormDataPart()
方法里面对请求头做些封装,一般不用再自己添加请求头信息
class UploadActivity extends AppCompatActivity {
private Platform mPlatform;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload);
mPlatform = Platform.get();
init();
}
private void init() {
Button button = (Button) findViewById(R.id.upload_activity_bt_img);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
uploadTextFile();
}
});
}
/**
* 上传一个40M的视频文件
*/
private void uploadTextFile() {
String filePath = Environment.getExternalStorageDirectory()
+ File.separator + Strings.FILE_PATH + File.separator + Strings.FILE_NAME;
File file = new File(filePath);
if (file.exists()) {
OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).build();
//MediaType 为全部类型
final MediaType mediaType = MediaType.parse("application/octet-stream");
//根据文件类型,将File装进RequestBody中
RequestBody fileBody = RequestBody.create(mediaType, file);
//将fileBody添加进MultipartBody
RequestBody requestBody = new MultipartBody.Builder()
.addFormDataPart("file", Strings.FILE_NAME, fileBody)
.build();
//Request请求对象
Request request = new Request.Builder().post(requestBody).url(Urls.UPLOAD_URL).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, final IOException e) {
mPlatform.execute(new Runnable() {
@Override
public void run() {
ToastUtils.show(UploadActivity.this, e.getMessage());
}
});
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
//请求结果
ResponseBody responseBody = null;
try {
//获取请求结果 ResponseBody
responseBody = response.body();
//获取字符串
final String info = responseBody.string();
mPlatform.execute(new Runnable() {
@Override
public void run() {
ToastUtils.show(UploadActivity.this, info);
}
});
} catch (Exception e) {//发生异常,失败回调
e.printStackTrace();
} finally {//记得关闭操作
if (null != responseBody) {
responseBody.close();
}
}
}
});
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Handler handler = new Handler(Looper.getMainLooper());
handler.removeCallbacksAndMessages(null);
}
}
上传的文件封装进RequestBody
中,创建RequestBody
需要用到MediaType
Content-type 常用对照表
封装带有文件的RequestBody
可以直接通过使用MutipartBody.Builder
来添加给Request
:
new MultipartBody.Builder().addFormDataPart("file", Strings.FILE_NAME, fileBody).build()
1.2 上传进度
直接使用了Toast
来显示当前进度
上传进度需要重写RequestBody
1.2.1 ProgressRequestBody
public class ProgressRequestBody extends RequestBody {
private File mFile;
private MediaType mMediaType;
private UploadProgress mUploadProgress;
public ProgressRequestBody(File mFile, MediaType mMediaType, UploadProgress mUploadProgress) {
this.mFile = mFile;
this.mMediaType = mMediaType;
this.mUploadProgress = mUploadProgress;
}
@Override
public long contentLength() throws IOException {
return mFile.length();
}
@Override
public MediaType contentType() {
return mMediaType;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
long max = contentLength();
long current = 0;
Source source = Okio.source(mFile);
Buffer buffer = new Buffer();
for (long readCount; (readCount = source.read(buffer, 1024 * 8)) != -1; current += readCount) {
sink.write(buffer, readCount);
int progress = (int) (100 * current / max);
mUploadProgress.progress(progress);
}
}
public interface UploadProgress {
void progress(int progress);
}
}
重写RequestBody
的3个方法,重点是writeTo(BufferedSink sink)
方法,根据当前的已写字节数来计算已经上传的百分比
里面用到了Okio
-
Sink
用来写,相当于OutputStream
-
Source
用来读,相当于InputStream
-
Buffer
,缓冲,Okio
高效读写的关键
Android 善用Okio简化处理I/O操作
Android Okhttp之Okio解析
OKio
需要单独再进行学习
1.2.2 使用
只需要将1.1
中的fileBody
,使用ProgressRequestBody
替换即可
// RequestBody fileBody = RequestBody.create(mediaType, file);
RequestBody fileBody = new ProgressRequestBody(file, mediaType, new ProgressRequestBody.UploadProgress() {
@Override
public void progress(final int progress) {
mPlatform.execute(new Runnable() {
@Override
public void run() {
ToastUtils.show(UploadActivity.this, progress + "%");
}
});
}
});
Okio
的读写操作不在UI
线程,不可直接更新UI
上传多个文件,只需要将多个文件分别都封装进RequestBody
中,多次使用MultipartBody.Builder()
的addFormDataPart()
方法都加入上传任务中就可以
new MultipartBody.Builder()
.addFormDataPart("file", fileName1, fileBody1)
.addFormDataPart("file", fileName2, fileBody2)
...
.addFormDataPart("file", fileNameX, fileBodyX)
.build();
Okio
,OkHttp
真的是超级好用,超级强大
2. 后台Web应用代码
自己学习写了一段最基础的上传后台部分的代码
- 添加
apache
的commons-fileupload
的Maven
依赖 - 重写
doPost()
方法 - 在
web.xml
,添加Servlet
映射
OKHttpServlet
public class OKHttpServlet extends HttpServlet {
private ServletFileUpload mServletFileUpload;
/*
* 初始化 ServletFileUpload
*/
private void initUp() {
if (null == mServletFileUpload) {
File file
= (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
mServletFileUpload = new ServletFileUpload(new DiskFileItemFactory(
DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD, file
));
mServletFileUpload.setFileSizeMax(1024L * 1024 * 100);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
initUp();
resp.setHeader("Content-Type", "text/html;charset=utf-8");//指定编码
Map> fileItemListMap = mServletFileUpload.parseParameterMap(req);
for (Map.Entry> entry : fileItemListMap.entrySet()) {
List fileItemList = entry.getValue();
if (!fileItemList.isEmpty()) {
for (FileItem fileItem : fileItemList) {
if (!fileItem.isFormField()) {//取非表单属性 ,也就是文件
String fileName =
FilenameUtils.getName(new String(fileItem.getName().getBytes(), "utf-8"));
InputStream inputStream = fileItem.getInputStream();
File file = new File(Stirngs.FILE_PATH, fileName);
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] bytes = new byte[1024 * 8];
int len;
while ((len = inputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, len);
fileOutputStream.flush();
}
fileOutputStream.close();
PrintWriter writer = resp.getWriter();
writer.print(fileName + " ----> ok!!!!!");//成功 ,返回个响应
writer.flush();
}
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
自己动手丰衣足食,代码虽烂,但这样不用使用别人的接口来实现上传了,哈哈
3. 最后
有错误,请指出
共勉 :)