multpart多文件上传总结

  • Post author:
  • Post category:其他



multipart/form-data

在最初的http协议中,没有定义上传文件的Method,为了实现这个功能,http协议组改造了post请求,添加了一种post规范,设定这种规范的Content-Type为multipart/form-data;boundary=${bound},其中${bound}是定义的分隔符,用于分割各项内容(文件,key-value对),OkHttp会自动生成boundary,无需手动编写规则

Retrofit其实是个网络代理框架,负责封装请求,然后把请求分发给http协议具体实现者httpclient,retrofit默认的httpclient是okhttp

Retrofit会根据注解封装网络请求,待httpclient请求完成后,把原始response内容通过转化器(converter)转化成我们需要的对象(object),在SsResponse中通过泛型注入


代码实现

本文分别用Retrofit和okhttp实现multipart/form-data的多文件上传:

  • Response返回类型定义如下,body为返回的具体内容,格式为json字符
public class Response {
    public final Map<String, String> headers;
    public final String body;
    public final int code;
    public final String msg;

    public Response(Map<String, String> headers, String body, int code, String msg) {
        this.headers = headers;
        this.body = body;
        this.code = code;
        this.msg = msg;
    }
}
  • Retrofit实现

    • Retrofit构建
 Retrofit retrofit = new Retrofit.Builder()
            .client(client) //设置OKHttpClient   
            .baseUrl(BASE_URL)  //设置baseUrl, baseUrl必须后缀"/"
            .addConverterFactory(GsonConverterFactory.create())  //添加Gson转换器
            .build();
  • 接口定义,有两种定义方式:

    • Retrofit会判断@Body的参数类型,如果参数类型为MultipartBody,则Retrofit不做包装处理,直接丢给okhttp3处理。因为MultipartBody是继承RequestBody,因此Retrofit不会自动包装这个对象。
    • Retrofit会判断@Part的参数类型,如果参数类型为MultipartBody.Part,则Retrofit会把RequestBody封装成MultipartBody,再把Part添加到MultipartBody。
public interface RetrofitNetApi {
        /**
         * 通过 MultipartBody和@Body作为参数来上传     
         * @param body MultipartBody包含多个Part     
         * @return 状态信息
         */
        @POST
        Call<String> uploadFile(@Url String url, @Body MultipartBody body);

        /**
         * 通过 List<MultipartBody.Part> 传入多个part实现多文件上传
         * @param parts
         * @return 状态信息
         */
        @Multipart
        @POST
        Call<String> uploadFile(@Url String url, @Part List<MultipartBody.Part> parts);
    }
  • 接口实现
// 方式1
   /**
    * @param url 上传文件地址
    * @param filePaths 上传文件绝对路径,支持多文件上传
    * @return Response
    * @throws Exception
    */
    public Response uploadFile(String url, List<String> filePaths) throws Exception {
        RetrofitNetApi netApi = retrofit.create(RetrofitNetApi.class);
        MultipartBody.Builder builder = new MultipartBody.Builder();
        for (String filePath : filePaths) {
            File file = new File(filePath);
            builder.addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file));
        }
        MultipartBody body = builder.build();
        SsResponse<String> ret = netApi.uploadFile(url, body).execute();
        return new Response(convertHeaders(ret.headers()), ret.body(), ret.code(), ret.raw().getReason());
    }

    // 方式2
    /**
     * @param url 上传文件地址
     * @param filePaths 上传文件绝对路径,支持多文件上传
     * @return Response
     * @throws Exception
     */
    public Response uploadFile(String url, List<String> filePaths) throws Exception {
        RetrofitNetApi netApi = retrofit.create(RetrofitNetApi.class);
        List<MultipartBody.Part> parts = new ArrayList<>(filePaths.size());
        for (String filePath : filePaths) {
            File file = new File(filePath);
            MultipartBody.Part part = MultipartBody.Part.createFormData("file", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file));
            parts.add(part);
        }
        SsResponse<String> ret = netApi.uploadFile(url, parts).execute();
        return new Response(convertHeaders(ret.headers()), ret.body(), ret.code(), ret.raw().getReason());
    }

    // convertHeaders实现
    private Map<String, String> convertHeaders(List<Header> headers) {
        HashMap<String, String> map = new HashMap<>();
        if (!ListUtils.isEmpty(headers)) {
            for (Header header : headers) {
                map.put(header.getName(), header.getValue());
            }
        }
        return map;
    }

  • okhttp3实现

    • OkHttpClient构建
OkHttpClient mApiClient = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS).build();
  • 具体实现
/**
 * @param url 上传文件地址
 * @param filePaths 上传文件绝对路径,支持多文件上传
 * @return Response
 * @throws Exception
 */
public Response uploadFile(@NonNull String url, @NonNull List<String> filePaths) throws Exception {
    MultipartBody.Builder builder = new MultipartBody.Builder();
    for (String filePath : filePaths) {
        File file = new File(filePath);
        builder.addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file));
    }
    RequestBody body = builder.build();
    Request request = new Request.Builder()
            .url(url)
            .post(body)
            .build();
    okhttp3.Response res = mApiClient.newCall(request).execute();
    return new Response(headers(res.headers()),
            res.code() == 200 ? res.body().string() : null, res.code(), res.message());
}

// res.headers转换为Map
private Map<String, String> headers(Headers headers) {
    if (headers == null) {
        return null;
    }
    Map<String, String> map = new HashMap<>();
    for (String name : headers.names()) {
        map.put(name, headers.get(name));
    }
    return map;
}



版权声明:本文为weixin_41480546原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。