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
相关产品推荐
相关产品推荐

