Retrofit 2.0 单文件、多文件上传

最近项目空档期,学习了一下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。

@Part,@PartMap:用于POST文件上传 
其中@Part MultipartBody.Part代表文件,@Part(“key”) RequestBody代表参数 
需要添加@Multipart表示支持文件上传的表单,Content-Type: multipart/form-data

一、单文件上传:

@Multipart
@POST("UploadServer")
Call> 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> call = service.upload(description, body);
    call.enqueue(new Callback>() {
        @Override
        public void onResponse(Call> call,
                               Response> response) {
            System.out.println("success");
            Logger.e("success:" + response.body().getResultMessage());
        }

        @Override
        public void onFailure(Call> call, Throwable t) {
            t.printStackTrace();
            Logger.e("error:" + t.getMessage());
        }
    });
}

第二、多文件上传:

定义一个上传的网络请求Service:

/**
 * 通过 List 传入多个part实现多文件上传
 * @param parts 每个part代表一个
 * @return 状态信息
 */
@Multipart
@POST("UploadServer")
Call> uploadFilesWithParts(@Part() List parts);


/**
 * 通过 MultipartBody和@body作为参数来实现多文件上传
 * @param multipartBody MultipartBody包含多个Part
 * @return 状态信息
 */
@POST("UploadServer")
Call> uploadFileWithRequestBody(@Body MultipartBody multipartBody);

由上可知,有两种方式实现上传

  • 使用@Multipart注解方法,并用@Part注解方法参数,类型是List
  • 不使用@Multipart注解方法,直接使用@Body注解方法参数,类型是okhttp3.MultipartBody

可以看到,无论方法参数类型是MultipartBody.Part还是MultipartBody,这些类都不是Retrofit的类,而是okhttp实现上传的源生类。

写好service接口后,来看看怎么构造MultipartBody
可以写一个方法,用于把File对象转化成MultipartBody:

public static MultipartBody filesToMultipartBody(List 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 filesToMultipartBodyParts(List files) {
    List 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 mFileList) {
    MultipartBody body = MultipartBuilder.filesToMultipartBody(mFileList);
    Api.getDefault().uploadFileWithRequestBody(body)
            .enqueue(new Callback>() {
                @Override
                public void onResponse(Call> call, Response> response) {
                    Logger.e("onResponse--" + response.body().getResultMessage());
                    if (response.isSuccessful()) {
                        BaseResponse body = response.body();
                        Logger.e("onResponse----success" );
                    } else {
                        Log.d(TAG,"上传失败");
                        Logger.e("onResponse----fail" );
                    }
                }

                @Override
                public void onFailure(Call> 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 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(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!


源码下载






你可能感兴趣的:(Android)