1. Retrofit 文件上传环境搭建:Gradle 依赖、OkHttp 日志拦截器、Multipart 转换器配置

文件上传是移动端高频场景,Retrofit 配合 OkHttp 是 Android 生态中最成熟的技术栈。本章聚焦搭建支持 multipart/form-data 上传的基础环境,包括依赖声明、请求日志可视化和 Multipart 转换器注册。完成本章后,你将获得一个可调试、可扩展的上传框架起点。

1.1 基本原理

Retrofit 本身只负责将 Java 接口转换为 HTTP 请求,实际网络调用由 OkHttp 执行。文件上传需要使用 Multipart 编码,将文件二进制数据分割成多个部分(Part)发送。关键组件关系如下:

  • OkHttpClient:底层 HTTP 引擎,提供连接池、超时、拦截器(如日志)能力。
  • LoggingInterceptor:OkHttp 内置或自建的拦截器,输出请求/响应细节,便于调试上传数据。
  • MultipartBody.Part:Retrofit 的 @Multipart 注解配合 @Part 参数,自动将文件封装为多部分请求体。
  • ConverterFactory:必须注册支持 RequestBody 或 MultipartBody.Part 的转换器(通常使用内置的 GsonConverterFactory 仅用于 JSON,Multipart 本身不需额外转换器,但 Retrofit 默认支持 @Multipart 的 RequestBody)。
核心认识: Retrofit 的 @Multipart 注解与 @Part 搭配时,Retrofit 自动使用 MultipartBody.Part 的序列化逻辑,不需要额外引入第三方 Multipart 转换器库——但必须确保 OkHttp 版本与 Retrofit 兼容。
Retrofit 文件上传环境组件关系 Android App Retrofit 实例 OkHttpClient LoggingInterceptor Multipart 转换器 @Multipart API

1.2 实现流程

  1. 声明 Gradle 依赖:引入 Retrofit、OkHttp、日志拦截器核心库。
  2. 创建 OkHttpClient:配置日志拦截器级别(BODY 可看到文件 Part 的 Headers)。
  3. 构建 Retrofit 实例:关联 baseUrl、OkHttpClient、转换器工厂(只需 GsonConverterFactory 处理非 Upload 的返回即可)。
  4. 定义上传接口:使用 @Multipart 和 @Part 注解。
  5. 调用上传:通过 MultipartBody.Part.createFormData() 将文件转为 Part。
  6. 观察日志:验证请求头、Content-Type、文件元数据等。

1.3 配置与代码

Gradle 依赖(app/build.gradle)

dependencies {
    // Retrofit 核心
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    // 可选:用于解析 JSON 响应(非上传必需,但通常需要)
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    // OkHttp 日志拦截器
    implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
    // OkHttp 核心(retrofit 已间接依赖,但显式声明版本统一)
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}
版本一致性: Retrofit 2.x 要求 OkHttp 3.x+,当前推荐 OkHttp 4.x(Kotlin 版)。混合版本可能导致 NoClassDefFoundError,请确保 retrofit、okhttp、logging-interceptor 的 OkHttp 版本完全一致。

OkHttp 日志拦截器配置

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;

HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(loggingInterceptor)
        .connectTimeout(30, TimeUnit.SECONDS)
        .writeTimeout(60, TimeUnit.SECONDS)  // 上传大文件需要更长写入超时
        .readTimeout(60, TimeUnit.SECONDS)
        .build();

Multipart 转换器与 Retrofit 实例构建

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://your-server.com/api/")
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

// 定义上传接口
public interface UploadApi {
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadFile(
            @Part MultipartBody.Part filePart,
            @Part("description") RequestBody description
    );
}
关键点: Retrofit 的 @Multipart 方法中,@Part 参数类型必须是 MultipartBody.Part、RequestBody 或 okhttp3.MultipartBody.Part。不需要手动注册“Multipart转换器”,Retrofit 内置了对 RequestBody 和 MultipartBody.Part 的支持。但若需要返回 JSON,则必须注册 GsonConverterFactory。

1.4 调试

日志拦截器级别设为 BODY 后,可在 Logcat 中观察到完整的请求体结构。上传文件时典型日志输出:

--> POST /api/upload HTTP/1.1
Content-Type: multipart/form-data; boundary=xxx
Content-Length: 1234567

--xxx
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg

[binary data omitted]
--xxx
Content-Disposition: form-data; name="description"
Content-Length: 13

Sample photo
--xxx--
--> END POST (binary body omitted)

<-- 200 OK (response...)
调试技巧: 若日志中看不到文件二进制明细(太大时 OkHttp 会省略),可改为 Level.HEADERS 仅查看 Content-Type 和文件名,避免日志爆炸。上传失败时重点关注 Content-Type 是否正确(必须是 multipart/form-data)、boundary 是否被服务器识别。

1.5 常见错误

错误现象原因解决方案
java.lang.IllegalArgumentException: @Part annotation must supply a part name or use MultipartBody.Part使用 @Part 时没有指定表单键名,且参数不是 MultipartBody.Part 类型。将文件参数改为 MultipartBody.Part,或使用 @Part("file") RequestBody。
NoSuchMethodError: okhttp3.MultipartBody$Builder.addFormDataPartOkHttp 版本与 Retrofit 不兼容(常见于 OkHttp 3.x 与 4.x 混用)。统一 OkHttp 版本为 4.x,并确保 logging-interceptor 同版本。
网络请求无日志输出未引入 logging-interceptor 或未添加到 OkHttpClient。检查依赖是否完整,并确认 addInterceptor(loggingInterceptor) 已调用。
文件上传超时默认超时 10 秒不足以传输大文件。显式设置 writeTimeout(60, TimeUnit.SECONDS)。

1.6 性能与稳定性优化

  • 日志级别按环境切换: 线上环境使用 Level.HEADERS 或 Level.NONE,避免打印文件二进制造成性能损耗和日志泄露。
  • 复用 OkHttpClient: 全局只创建一次 OkHttpClient 实例,避免频繁建立连接。
  • 文件大小限制: 在应用层对大文件做分片或压缩,防止 OOM 和超时。
  • 依赖版本锁定: 使用 implementation platform('com.squareup.okhttp3:okhttp-bom:4.12.0') 统一管理 OkHttp 家族版本,避免冲突。