@FormUrlEncoded
@POST("upload")
Call<ResponseBody> uploadParams(@Field("username")String username,@Field("token")String token);
@FormUrlEncoded
@POST("upload")
Call<ResponseBody> uploadParams(@FieldMap Map<String,String> map);   
@POST("upload")
Call<ResponseBody> uploadParams(@Body RequestBody body);

2. 文件上传

单文件上传
 @Multipart
 @POST("upload")
Call<ResponseBody> uploadOneFile(@Part MultipartBody.Part body);

调用方式:

File file = new File("");
RequestBody fileRQ = RequestBody.create(MediaType.parse("image/*"), file);
MultipartBody.Part part MultipartBody.Part.createFormData("picture", file.getName(), fileRQ);
Call<ResponseBody> uploadCall = downloadService.uploadOneFile(part);
多文件上传
@Multipart
@POST("upload")
Call<ResponseBody> uploadFiles(@PartMap Map<String, RequestBody> map);
RequestBody fb = RequestBody.create(MediaType.parse("text/plain"), "hello,retrofit");
RequestBody fileTwo = RequestBody.create(MediaType.parse("image/*"), new File(Environment.getExternalStorageDirectory()
                + file.separator + "original.png"));
Map<String, RequestBody> map = new HashMap<>();
//这里的key必须这么写,否则服务端无法识别
map.put("file\"; filename=\""+ file.getName(), fileRQ);
map.put("file\"; filename=\""+ "2.png", fileTwo);
Call<ResponseBody> uploadCall = downloadService.uploadFiles(map);
@Multipart
@POST("upload")
Call<ResponseBody> uploadFiles(@Part List<MultipartBody.Part> parts);
RequestBody fileRQ = RequestBody.create(MediaType.parse("image/*"), file);
MultipartBody.Part part = MultipartBody.Part.createFormData("picture", file.getName(), fileRQ);
RequestBody fb = RequestBody.create(MediaType.parse("text/plain"), "hello,retrofit");
RequestBody fileTwo = RequestBody.create(MediaType.parse("image/*"), new File(Environment.getExternalStorageDirectory()
                + file.separator + "original.png"));
MultipartBody.Part two=MultipartBody.Part.createFormData("one","one.png",fileTwo);
List<MultipartBody.Part> parts=new ArrayList<>();
parts.add(part);
parts.add(two);
 Call<ResponseBody> uploadCall = downloadService.uploadFiles(parts);

3. 文件+参数混合上传

@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(@Part("body") RequestBody body, @Part MultipartBody.Part file);
MultipartBody.Part part = MultipartBody.Part.createFormData("picture", file.getName(), fileRQ);
RequestBody fb =RequestBody.create(MediaType.parse("text/plain"), "hello,retrofit");
Call<ResponseBody> uploadCall = downloadService.uploadFile(fb,part);
 @POST("upload")
 Call<ResponseBody> uploadFile(@Body RequestBody body);
        File file = new File("path");
        RequestBody fileRQ = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        MultipartBody.Part part = MultipartBody.Part.createFormData("picture", file.getName(), fileRQ);
        RequestBody body=new MultipartBody.Builder()
                .addFormDataPart("userName","lange")
                .addFormDataPart("token","dxjdkdjkj9203kdckje0")
                .addFormDataPart("header",file.getName(),fileRQ)
                .build();
        Call<ResponseBody> uploadCall = downloadService.uploadFile(body);
        uploadCall.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                Log.i("upload", response.isSuccessful() + "");
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
        });

二、Okhttp拦截器添加

Retrofit配置

import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
class RetrofitHelper private constructor() {
    private var okHttpClient: OkHttpClient
    private var retrofit: Retrofit
    private val headerInterceptor: Interceptor
    private val logInterceptor: HttpLoggingInterceptor
    private object SingletonHolder {
        val helper = RetrofitHelper()
    companion object {
        private var BASE_URL = ""
        private const val DEFAULT_CONNECT_TIME = 10
        private const val DEFAULT_WRITE_TIME = 15
        private const val DEFAULT_READ_TIME = 15
        const val TOKEN_KEY = "token"
        const val UID_KEY = "uid"
        const val LANGUAGE_KEY = "language"
        const val COUNTRY_KEY = "country"
        const val SUCCESS_CODE = 1
        val context = Application.context
        val instance = SingletonHolder.helper
    init {
        headerInterceptor = Interceptor {
            val request = it.request().newBuilder()
                .addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
                .addHeader("Connection", "keep-alive")
                .addHeader("timestamp", (System.currentTimeMillis() / 1000).toString())  
                .build()
            it.proceed(request)
        SslUtils.initSslSocketFactorySingleByAll(context.applicationContext)
        logInterceptor = HttpLoggingInterceptor()
        logInterceptor.level = HttpLoggingInterceptor.Level.BODY
        okHttpClient = OkHttpClient.Builder()
            .connectTimeout(DEFAULT_CONNECT_TIME.toLong(), TimeUnit.SECONDS) //连接超时时间
            .writeTimeout(DEFAULT_WRITE_TIME.toLong(), TimeUnit.SECONDS) //设置写操作超时时间
            .readTimeout(DEFAULT_READ_TIME.toLong(), TimeUnit.SECONDS) //设置读操作超时时间
            .addInterceptor(headerInterceptor)
            .addInterceptor(LoggingInterceptor())
            .sslSocketFactory(SslUtils.sSLSocketFactory,SslUtils.trustManager)
            .build()
        retrofit = Retrofit.Builder()
            .client(okHttpClient) //设置使用okHttp网络请求
            .baseUrl(BASE_URL) //设置服务器路径
            .addConverterFactory(GsonConverterFactory.create()) //添加转化库,默认是Gson
            .build()
    fun <T> create(service: Class<T>?): T {
        return retrofit.create(service)
import java.io.IOException;
import java.nio.charset.Charset;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MultipartBody;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
public class LoggingInterceptor implements Interceptor {
    private static final String TAG = "LoggingInterceptor";
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        Response response = chain.proceed(original);
        LogUtil.e(TAG, String.format("...\n请求链接:%s\n请求方式:%s\n请求头:\n%s\n请求参数:\n%s\n响应码:%s\n请求响应:\n%s", original.url(),
                original.method(), getRequestHeaders(original), getRequestInfo(original), response.code(), getResponseInfo(response)));
        return response;
     * 打印请求头
     * @param request 请求的对象
    private String getRequestHeaders(Request request) {
        String str = "";
        if (request == null) {
            return str;
        Headers headers = request.headers();
        if (headers == null) {
            return str;
        return headers.toString();
     * 打印请求消息
     * @param request 请求的对象
    private String getRequestInfo(Request request) {
        String str = "";
        if (request == null) {
            return str;
        RequestBody requestBody = request.body();
        if (requestBody == null) {
            return str;
        if (requestBody instanceof MultipartBody) {
            return "MultipartBody不显示";
        try {
            Buffer bufferedSink = new Buffer();
            requestBody.writeTo(bufferedSink);
            Charset charset = Charset.forName("utf-8");
            str = bufferedSink.readString(charset);
        } catch (Exception e) {
            e.printStackTrace();
        return str;
     * 打印返回消息
     * @param response 返回的对象
    private String getResponseInfo(Response response) {
        String str = "";
        if (response == null || !response.isSuccessful()) {
            return str;
        ResponseBody responseBody = response.body();
        long contentLength = responseBody.contentLength();
        BufferedSource source = responseBody.source();
        try {
            source.request(Long.MAX_VALUE); // Buffer the entire body.
        } catch (Exception e) {
            e.printStackTrace();
        Buffer buffer = source.buffer();
        Charset charset = Charset.forName("utf-8");
        if (contentLength != 0) {
            str = buffer.clone().readString(charset);
        return str;

发送+接收数据拦截

import android.content.Context;
import android.text.TextUtils;
import com.google.gson.Gson;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.HashMap;
import java.util.Set;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
 * 处理拦截
public class SafeInterceptor implements Interceptor {
    private String TAG = "SafeInterceptor";
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        //指定域名不拦截
        if (request.url().toString().endsWith("XXXX")) {
            return chain.proceed(request);
        HashMap<String, String> needSignMap = new HashMap<>();
        //1.获取head数据
        getRequestHeaders(request, needSignMap);
        //2.获取queryMap数据
        getQuery(request, needSignMap);
        //3.获取mediaType:x-www-form-urlencoded,Body数据
        //4.获取mediaType:multipart/form-data,value 类型和 file 类型。
        getBody(request, needSignMap);
        Request.Builder newBuilder = request.newBuilder();
        newBuilder.addHeader("X-Unique-Sign", sign);
        //6.body数据加密
        RequestBody requestBody = request.body();
        if (requestBody != null) {
            if (requestBody instanceof FormBody) {
                newBuilder.post(requestBody);
            } else if (requestBody instanceof MultipartBody) {
                newBuilder.post(requestBody);
            } else {
                String postBodyString = bodyToString(requestBody);
                LogUtil.e(TAG, "请求参数:" + postBodyString);
                if (!TextUtils.isEmpty(postBodyString)) {
                    String type = request.method();
                    if ("Post".equalsIgnoreCase(type)){
                        newBuilder.post(RequestBody.create(MediaType.parse("text/plain;charset=UTF-8"), allBase));
                    }else if ("Put".equalsIgnoreCase(type)){
                        newBuilder.put(RequestBody.create(MediaType.parse("text/plain;charset=UTF-8"), allBase));
        //7.返回数据
        request = newBuilder.build();
        Response response = chain.proceed(request);
        return getResponseString(response, sign, app_secret);
    private void getBody(Request request, HashMap<String, String> needSignMap) {
        RequestBody requestBody = request.body();
        if (requestBody == null) return;
        MediaType type = requestBody.contentType();
        if (type != null) {
            LogUtil.e(TAG, "接口请求Type:" + type);
            if (requestBody instanceof FormBody) {
                for (int i = 0; i < ((FormBody) requestBody).size(); i++) {
                    String name = ((FormBody) requestBody).encodedName(i);
                    if (!"file".equalsIgnoreCase(name)) {
                        needSignMap.put(name, ((FormBody) requestBody).encodedValue(i));
            } else if (requestBody instanceof MultipartBody) {
                MultipartBody body = (MultipartBody) requestBody;
                HashMap<String, String> params = new HashMap<>();
                HashMap<String, String> files = new HashMap<>();
                for (MultipartBody.Part part : body.parts()) {
                    RequestBody body1 = part.body();
                    Headers headers = part.headers();
                    if (headers != null && headers.size() > 0) {
                        String[] split = headers.value(0).replace(" ", "").replace("\"", "").split(";");
                        if (split.length == 2) {
                            String[] keys = split[1].split("=");
                            try {
                                if (keys.length > 1 && body1.contentLength() < 1024) {
                                    String key = keys[1];
                                    String value = "";
                                    Buffer buffer = new Buffer();
                                    body1.writeTo(buffer);
                                    value = buffer.readUtf8();
                                    params.put(key, value);
                            } catch (IOException e) {
                                LogUtil.e(TAG,"getBody multipart:"+e.getMessage());
                        } else if (split.length == 3) {
                            String fileKey = "";
                            String fileName = "";
                            String[] keys = split[1].split("=");
                            String[] names = split[2].split("=");
                            if (keys.length > 1) fileKey = keys[1];
                            if (names.length > 1) fileName = names[1];
                            files.put(fileKey, fileName);
                LogUtil.e(TAG,"文本参数:"+params);
                params.remove("file");
                LogUtil.e(TAG,"文件参数:"+files);
                needSignMap.putAll(params);
            } else {
                if (type.toString().contains("x-www-form-urlencoded")) {
                    HashMap<String, String> urlencodedMap = new HashMap<>();
                    //buffer流
                    Buffer buffer = new Buffer();
                    try {
                        requestBody.writeTo(buffer);
                        String oldParamsJson = buffer.readUtf8();
                        Gson gson = new Gson();
                        urlencodedMap = gson.fromJson(oldParamsJson, HashMap.class);  //原始参数
                        needSignMap.putAll(urlencodedMap);
                    } catch (IOException e) {
                        LogUtil.e(TAG,"getBody form:"+e.getMessage());
    private void getQuery(Request request, HashMap<String, String> needSignMap) {
        Set<String> paramterSet = request.url().queryParameterNames();
        for (String itmeName : paramterSet) {
            String itemValue = request.url().queryParameter(itmeName);
            needSignMap.put(itmeName, itemValue);
    private void getRequestHeaders(Request request, HashMap<String, String> signMap) {
        if (request != null) {
            Headers headers = request.headers();
            if (headers != null) {
                String timestampValue = headers.get("timestamp");
                if (!TextUtils.isEmpty(timestampValue)) {
                    signMap.put("timestamp", timestampValue);
    private String bodyToString(final RequestBody request) {
        try {
            final RequestBody copy = request;
            final Buffer buffer = new Buffer();
            if (copy != null){
                copy.writeTo(buffer);
            } else {
                return "";
            return buffer.readUtf8();
        } catch (final IOException e) {
            LogUtil.e(TAG, "bodyToString IOException");
            return "";
    /*处理响应*/
    private Response getResponseString(Response response, String sign, String app_secret) throws UnsupportedEncodingException {
        ResponseBody responseBody = response.body();
        if (responseBody != null) {
            long contentLength = responseBody.contentLength();
            //判断返回内容中data加密
            if (bodyEncoded(response.headers())) {
                BufferedSource source = responseBody.source();
                try {
                    source.request(Long.MAX_VALUE); // Buffer the entire body.
                } catch (IOException e) {
                    LogUtil.e(TAG, "getResponseString IOException:"+ e.getMessage());
                Buffer buffer = source.buffer();
                Charset charset = Charset.forName("UTF-8");
                MediaType contentType = responseBody.contentType();
                if (contentType != null) {
                    try {
                        charset = contentType.charset(Charset.forName("UTF-8"));
                    } catch (UnsupportedCharsetException e) {
                        LogUtil.e(TAG, e.getMessage());
                if (contentLength != 0) {
                    String res = buffer.clone().readString(charset);
//                    LogUtil.e(TAG, "响应数据:" + res);
                    BaseAResponse baseAsResponse = new Gson().fromJson(res, BaseAResponse.class);
                    String secretData = baseAesResponse.getData();
                    String result = secretData;
                    if (!TextUtils.isEmpty(secretData)) {
                        String contentEncoding = response.headers().get("Algorithm");
                      /*  if (!TextUtils.isEmpty(result)){
                            BaseResponse response = new Gson().fromJson(result, BaseEcdhResponse.class);
                    } else {
                        result = res;
                    LogUtil.e(TAG, "解密数据:" + result);
                    MediaType mediaType = responseBody.contentType();
                    return response.newBuilder().body(ResponseBody.create(
                            mediaType,
                            result
                    )).build();
        return response;

三、MultipartBody内部数据解析

直接上代码示例

kotlin

 if (requestBody is MultipartBody) {
            val params = mutableMapOf<String, String>()
            val files = mutableMapOf<String, String>()
            requestBody.parts().forEach {
                val body = it.body()
                it.headers()?.let {
                    val header = it.value(0)
                    val split = header.replace(" ", "").replace("\"", "").split(";")
                    when (split.size) {
                        2 -> {
                            //文本参数
                            val keys = split[1].split("=")
                            if (keys.size > 1 && body.contentLength() < 1024) {
                                val key = keys[1]
                                val buffer = Buffer()
                                body.writeTo(buffer)
                                val value = buffer.readUtf8()
                                params[key] = value
                        3 -> {
                            val fileKeys = split[1].split("=")
                            val fileKey = if (fileKeys.size > 1) {
                                fileKeys[1]
                            } else ""
                            val nameValue = split[2].split("=")
                            val fileName = if (nameValue.size > 1) nameValue[1] else ""
                            files[fileKey] = fileName
            println("文件-->$files")
            println("文本-->$params")
if (requestBody instanceof MultipartBody) {
            MultipartBody body = (MultipartBody) requestBody;
            Map<String, String> params = new HashMap<>();
            Map<String, String> files = new HashMap<>();
            for (MultipartBody.Part part : body.parts()) {
                RequestBody body1 = part.body();
                Headers headers = part.headers();
                if (headers != null && headers.size() > 0) {
                    String[] split = headers.value(0).replace(" ", "").replace("\"", "").split(";");
                    if (split.length == 2) {
                        String[] keys = split[1].split("=");
                        if (keys.length > 1 && body1.contentLength() < 1024) {
                            String key = keys[1];
                            String value = "";
                            Buffer buffer = new Buffer();
                            body1.writeTo(buffer);
                            value = buffer.readUtf8();
                            params.put(key, value);
                    } else if (split.length == 3) {
                        String fileKey = "";
                        String fileName = "";
                        String[] keys = split[1].split("=");
                        String[] names = split[2].split("=");
                        if (keys.length > 1) fileKey = keys[1];
                        if (names.length > 1) fileName = names[1];
                        files.put(fileKey, fileName);
            System.out.println("文本参数-->" + params);
            System.out.println("文件参数-->" + files);
                                    ContentType,也称为媒体类型或MIME类型,用于描述网络请求和响应中的内容类型。text/plain:普通文本text/html:HTML文档:JSON数据:XML数据image/jpeg:JPEG图片image/png:PNG图片:用于文件上传本文主要关注,因为它通常用于表单提交,特别是包含文件上传的表单Android 移动开发当中免不了要访问网络,访问网络的方式相信每个人都会有自己不同的方法,你可以自己封装网络请求,也可以用网上别人开源的框架。如果开源的框架当中,目前最受欢迎的可要数Retrofit了,而使用Retrofit中最麻烦也就是上传图片或者文件了,本文就对如何使用Retrofit同时上传多参数和多图片文件做详细的演示,并贴出相应实例代码。
在正式本文的讲解之前,先说明一下,基于现在R
                                    使用MultipartBody上传多个文件
RequestBody.create用于接收MediaType.parse
MediaType.parse(“text/plain”)  指定文件上传的类型
Request  创建请求对象
url  参数为域名
post(multipartBody)   参数是需要上传的文件和类型
Builder 构建器
okHttpClient.newCall(request);  获取Call(准备好请求的对象)  参数是:Request  请求对象
Call  准备好请求的
                                    在“Header”里面添加“Content-Type”好似无效一样,后台并不能接收到数据,接口一直是超时的状态,在RequestBody设置“Content-Type”才得以解决;
                                    相信大多数人在写文件上传下载代码的时候,都不太明白MediaType的含义。这里详细列出MediaType含义。以及对应解释说明。类型描述| — | — || text/html | HTML格式 || text/plain | 纯文本格式,空格转换为 “+” 加号,不对特殊字符编码 || text/xml | XML格式 || text/x-markdown | Markdown格式 || image/gif | gif图片格式 || image/jpeg | jpg图片格式 |
@FormUrlEncoded
@POST("upload")
Call<ResponseBody> uploadParams(@Field("username")String username,@Field("token")String token);
2. 多个参数上传
@FormUrlEncoded
@POST("upload")
Call<ResponseBody> uploadParams(@FieldMap Map<String,
                                    然后controller层的传参需要用@RequestParam或者直接一个请求的实体类,如果使用实体类,千万不要加@RequestBody,不然结合上传文件会失效,上传文件使用。有时候需要对接一些接口,而且接口传参不仅需要各种类型的参数,甚至还要上传文件,所以遇到挺多坑,用postman的生成代码也不好用,于是就有了这篇文章。@RequestPart("file") MultipartFile file进行传参。话不多说,我们直接上代码。首先是service层。