You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin中如何用Retrofit 2发送含字符串与文件的ArrayList请求

在Android Kotlin中用Retrofit 2发送含字符串与文件的ArrayList请求

你的核心问题是处理嵌套数组结构的Multipart请求——每个数组元素同时包含文件(如licenses[0].license)和字符串参数(如licenses[0].expDate),原接口的写法没法覆盖这种组合结构,下面是具体实现方案:

1. 修正ApiService接口

推荐用@PartMap传递所有字符串参数,配合单独的文件Part列表,这种方式能灵活处理嵌套参数命名,适配数组元素的结构:

import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Multipart
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Part
import retrofit2.http.PartMap
import org.json.JSONObject

interface ApiService {
    @Multipart
    @PUT("employees/{id}")
    suspend fun putEmployeeUpdate(
        @Path("id") id: Int,
        @PartMap params: Map<String, RequestBody>,
        @Part files: List<MultipartBody.Part>
    ): JSONObject
}

如果数组元素数量固定(比如仅1个license和1个insurence),也可以把参数明确列出,可读性更强:

@Multipart
@PUT("employees/{id}")
suspend fun putEmployeeUpdate(
    @Path("id") id: Int,
    @Part("name") name: RequestBody,
    @Part("phone") phone: RequestBody,
    @Part("companyName") companyName: RequestBody,
    @Part("abn") abn: RequestBody,
    @Part("service") service: RequestBody,
    @Part("email") email: RequestBody,
    @Part("type") type: RequestBody,
    @Part("locations[]") locations: List<RequestBody>,
    // License相关
    @Part("licenses[0].expDate") licenseExpDate: RequestBody,
    @Part("licenses[0].license") licenseFile: MultipartBody.Part,
    // Insurence相关
    @Part("insurences[0].expDate") insurenceExpDate: RequestBody,
    @Part("insurences[0].insurence") insurenceFile: MultipartBody.Part
): JSONObject

2. 构建请求参数

处理字符串参数

把所有字符串/数字参数转成RequestBody,注意嵌套参数命名要和请求负载完全一致:

import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody

// 基础字符串参数
val nameBody = "Employee one".toRequestBody("text/plain".toMediaTypeOrNull())
val phoneBody = "9876543210".toRequestBody("text/plain".toMediaTypeOrNull())
val companyNameBody = "ows".toRequestBody("text/plain".toMediaTypeOrNull())
val abnBody = "ows1234".toRequestBody("text/plain".toMediaTypeOrNull())
val serviceBody = 4.toString().toRequestBody("text/plain".toMediaTypeOrNull())
val emailBody = "souravc+1001@pitangent.com".toRequestBody("text/plain".toMediaTypeOrNull())
val typeBody = "INVITE".toRequestBody("text/plain".toMediaTypeOrNull())

// Locations数组(多元素时添加多个对应RequestBody)
val location1Body = 1.toString().toRequestBody("text/plain".toMediaTypeOrNull())

// 嵌套日期参数
val licenseExpDateBody = "2022-12-06".toRequestBody("text/plain".toMediaTypeOrNull())
val insurenceExpDateBody = "2022-12-06".toRequestBody("text/plain".toMediaTypeOrNull())

处理文件参数

把文件转成MultipartBody.Part,指定正确的参数名:

import okhttp3.MultipartBody
import java.io.File

// 假设已获取对应文件对象
val licenseFile = File("/path/to/license/file")
val insurenceFile = File("/path/to/insurence/file")

// 构建License文件Part
val licensePart = MultipartBody.Part.createFormData(
    name = "licenses[0].license",
    filename = licenseFile.name,
    body = licenseFile.asRequestBody("application/octet-stream".toMediaTypeOrNull())
)

// 构建Insurence文件Part
val insurencePart = MultipartBody.Part.createFormData(
    name = "insurences[0].insurence",
    filename = insurenceFile.name,
    body = insurenceFile.asRequestBody("application/octet-stream".toMediaTypeOrNull())
)

3. 发起请求

如果用@PartMap接口,把所有字符串参数放进Map后调用:

val paramsMap = mutableMapOf<String, RequestBody>().apply {
    put("name", nameBody)
    put("phone", phoneBody)
    put("companyName", companyNameBody)
    put("abn", abnBody)
    put("service", serviceBody)
    put("email", emailBody)
    put("type", typeBody)
    put("locations[]", location1Body)
    put("licenses[0].expDate", licenseExpDateBody)
    put("insurences[0].expDate", insurenceExpDateBody)
}

val filesList = listOf(licensePart, insurencePart)

// 调用接口(假设已初始化ApiService实例)
val response = apiService.putEmployeeUpdate(employeeId, paramsMap, filesList)

如果用参数明确列出的接口,直接传递对应参数即可:

val response = apiService.putEmployeeUpdate(
    id = employeeId,
    name = nameBody,
    phone = phoneBody,
    companyName = companyNameBody,
    abn = abnBody,
    service = serviceBody,
    email = emailBody,
    type = typeBody,
    locations = listOf(location1Body),
    licenseExpDate = licenseExpDateBody,
    licenseFile = licensePart,
    insurenceExpDate = insurenceExpDateBody,
    insurenceFile = insurencePart
)

关键注意点

  • 原接口问题:直接用@Part Array<MultipartBody.Part?>只能传递文件,无法绑定对应的expDate字符串参数,必须把每个数组元素的文件和字符串拆分开处理。
  • 参数命名必须和请求负载完全匹配(如licenses[0].expDate、locations[]),服务器才能正确解析。
  • 数组多元素时,只需添加对应命名的参数和文件Part即可(如licenses[1].license、licenses[1].expDate)。

内容的提问来源于stack exchange,提问作者Kajal Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 06:56:55