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 兼容。
1.2 实现流程
- 声明 Gradle 依赖:引入 Retrofit、OkHttp、日志拦截器核心库。
- 创建 OkHttpClient:配置日志拦截器级别(BODY 可看到文件 Part 的 Headers)。
- 构建 Retrofit 实例:关联 baseUrl、OkHttpClient、转换器工厂(只需 GsonConverterFactory 处理非 Upload 的返回即可)。
- 定义上传接口:使用
@Multipart和@Part注解。 - 调用上传:通过
MultipartBody.Part.createFormData()将文件转为 Part。 - 观察日志:验证请求头、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.addFormDataPart | OkHttp 版本与 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 家族版本,避免冲突。