最近项目空档期,学习了一下Retrofit 2.0文件上传,在网上查了好多资源,也踩了好多坑,还好经过努力,Retrofit2.0单文件、多文件上传的一个小demo弄出来了,记录下来方便以后自己查阅使用。
依赖:
compile 'com.android.support:appcompat-v7:24.2.0'
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
使用Retrofit 2.0 上传文件,需要使用:Part & PartMap。
<span style="font-family:Microsoft YaHei;font-size:18px;">@Part,@PartMap:用于POST文件上传
其中@Part MultipartBody.Part代表文件,@Part(“key”) RequestBody代表参数
需要添加@Multipart表示支持文件上传的表单,Content-Type: multipart/form-data</span>
一、单文件上传:
@Multipart
@POST("UploadServer")
Call<BaseResponse<String>> upload(@Part("description") RequestBody description,
@Part MultipartBody.Part file);
我们用这种方法提交单文件,下面是这个借口的使用方法:
创建Api类获取FileUploadService:
public class Api {
private static FileUploadService SERVICE;
private static final String BASE_URL = "http://192.168.1.100:8080/UploadFileServer/";
public static FileUploadService getDefault() {
if(SERVICE == null) {
OkHttpClient client = new OkHttpClient.Builder().build();
SERVICE = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build().create(FileUploadService.class);
}
return SERVICE;
}
}
在activity里调用接口:
private void uploadMonofile(String filename){
//先创建 service
FileUploadService service = Api.getDefault();
//构建要上传的文件
File file = new File(filename);
RequestBody requestFile =
RequestBody.create(MediaType.parse("application/otcet-stream"), file);
MultipartBody.Part body =
MultipartBody.Part.createFormData("aFile", file.getName(), requestFile);
String descriptionString = "This is a description";
RequestBody description =
RequestBody.create(
MediaType.parse("multipart/form-data"), descriptionString);
Call<BaseResponse<String>> call = service.upload(description, body);
call.enqueue(new Callback<BaseResponse<String>>() {
@Override
public void onResponse(Call<BaseResponse<String>> call,
Response<BaseResponse<String>> response) {
System.out.println("success");
Logger.e("success:" + response.body().getResultMessage());
}
@Override
public void onFailure(Call<BaseResponse<String>> call, Throwable t) {
t.printStackTrace();
Logger.e("error:" + t.getMessage());
}
});
}
第二、多文件上传:
定义一个上传的网络请求Service:
/**
* 通过 List<MultipartBody.Part> 传入多个part实现多文件上传
* @param parts 每个part代表一个
* @return 状态信息
*/
@Multipart
@POST("UploadServer")
Call<BaseResponse<String>> uploadFilesWithParts(@Part() List<MultipartBody.Part> parts);
/**
* 通过 MultipartBody和@body作为参数来实现多文件上传
* @param multipartBody MultipartBody包含多个Part
* @return 状态信息
*/
@POST("UploadServer")
Call<BaseResponse<String>> uploadFileWithRequestBody(@Body MultipartBody multipartBody);
由上可知,有两种方式实现上传
- 使用
@Multipart
注解方法,并用@Part
注解方法参数,类型是List<okhttp3.MultipartBody.Part>
- 不使用
@Multipart
注解方法,直接使用@Body
注解方法参数,类型是okhttp3.MultipartBody
可以看到,无论方法参数类型是MultipartBody.Part
还是MultipartBody
,这些类都不是Retrofit的类,而是okhttp实现上传的源生类。
MultipartBody
可以写一个方法,用于把
File
对象转化成MultipartBody
:
public static MultipartBody filesToMultipartBody(List<File> files) {
MultipartBody.Builder builder = new MultipartBody.Builder();
for (File file : files) {
// TODO: 16-4-2 这里为了简单起见,没有判断file的类型
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
builder.addFormDataPart("aFile", file.getName(), requestBody);
}
builder.setType(MultipartBody.FORM);
MultipartBody multipartBody = builder.build();
return multipartBody;
}
或者把File
转化成MultipartBody.Part
:
public static List<MultipartBody.Part> filesToMultipartBodyParts(List<File> files) {
List<MultipartBody.Part> parts = new ArrayList<>(files.size());
for (File file : files) {
// TODO: 16-4-2 这里为了简单起见,没有判断file的类型
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
MultipartBody.Part part = MultipartBody.Part.createFormData("aFile", file.getName(), requestBody);
parts.add(part);
}
return parts;
}
在Activity里调用接口
private void uploadMultifile(ArrayList<File> mFileList) {
MultipartBody body = MultipartBuilder.filesToMultipartBody(mFileList);
Api.getDefault().uploadFileWithRequestBody(body)
.enqueue(new Callback<BaseResponse<String>>() {
@Override
public void onResponse(Call<BaseResponse<String>> call, Response<BaseResponse<String>> response) {
Logger.e("onResponse--" + response.body().getResultMessage());
if (response.isSuccessful()) {
BaseResponse<String> body = response.body();
Logger.e("onResponse----success" );
} else {
Log.d(TAG,"上传失败");
Logger.e("onResponse----fail" );
}
}
@Override
public void onFailure(Call<BaseResponse<String>> call, Throwable t) {
Logger.e("onResponse----onFailure"+ t.getMessage());
}
});
}
至此客户端核心代码基本完成,
服务器核心代码:
需要用到两个重要的jar:commons-fileupload-1.2.1.jar commons-io-1.4.jar
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("有文件提交");
// 设置response的编码格式
response.setHeader("Content-type", "text/html;charset=UTF-8");
response.setCharacterEncoding("utf-8");
// 创建文件项目工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置文件上传路径
// String upload = this.getServletContext().getRealPath("/");
String upload = "D:/aa/image/";
// 获取系统默认的临时文件保存路径,该路径为Tomcat根目录下的temp文件夹
String temp = System.getProperty("java.io.tmpdir");
// 设置缓冲区大小为 5M
factory.setSizeThreshold(1024 * 1024 * 5);
// 设置临时文件夹为temp
factory.setRepository(new File(temp));
// 用工厂实例化上传组件,ServletFileUpload 用来解析文件上传请求
ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
// 解析结果放在List中
try {
List<FileItem> list = servletFileUpload.parseRequest(request);
for (FileItem item : list) {
String name = item.getFieldName();
InputStream is = item.getInputStream();
System.out.println("the current name is " + name);
if (name.contains("aFile")) {
try {
inputStream2File(is, upload + "\\" + /*
* System.
* currentTimeMillis
* () +
*/item.getName());
} catch (Exception e) {
e.printStackTrace();
}
} else {
String key = item.getName();
String value = item.getString();
System.out.println(key + "---" + value);
}
}
// out.write("success");
Gson gson = new Gson();
ResponseData data = new ResponseData<String>(200, "成功", "上传了文件");
String gsonStr = gson.toJson(data);
System.out.println("响应数据::" + gsonStr);
response.getWriter().print(gsonStr);
} catch (FileUploadException e) {
e.printStackTrace();
System.out.println("failure");
}
PrintWriter out = response.getWriter();
out.flush();
out.close();
}
// 流转化成字符串
public static String inputStream2String(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i = -1;
while ((i = is.read()) != -1) {
baos.write(i);
}
return baos.toString();
}
// 流转化成文件
public static void inputStream2File(InputStream is, String savePath)
throws Exception {
System.out.println("the file path is :" + savePath);
File file = new File(savePath);
InputStream inputSteam = is;
BufferedInputStream fis = new BufferedInputStream(inputSteam);
FileOutputStream fos = new FileOutputStream(file);
int f;
while ((f = fis.read()) != -1) {
fos.write(f);
}
fos.flush();
fos.close();
fis.close();
inputSteam.close();
}
至此一个Retrofit 2.0 上传单文件、多文件的小demo就完成了
注意:Retrofit 2.0 依赖不要使用:'com.squareup.retrofit2:retrofit:2.0.0-beta4' beta版,说多了都是泪啊。
参考:
http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1117
http://www.chenkaihua.com/2016/04/02/retrofit2-upload-multipart-files.html
还有参考其他的,不过忘记了。sorry!