如何用Retrofit2发送带图片的Multipart请求?413错误排查
问题描述
需要发送与Postman中相同的Multipart请求,使用Retrofit结合Coroutines开发,但服务器返回413(实体过大)错误,Postman发送相同请求却正常。相关代码如下:
Retrofit接口定义
@POST("ia") @Multipart suspend fun getMeasurement( @HeaderMap authenticationHeaders: ApiHeaders, @Part("userId") userId: RequestBody, @Part("sessionId") sessionId: RequestBody, @Part file: MultipartBody.Part, @Part file2: MultipartBody.Part, @Part file3: MultipartBody.Part, @Part file4: MultipartBody.Part, ): Response<MeasurementResponse>
创建MultipartBody.Part的代码
fun createImageRequestBody(imageUri: Uri, fileName: String): MultipartBody.Part? { context.contentResolver?.query(imageUri, null, null, null, null)?.use { if (it.moveToFirst()) { val picturePath = it.getString(it.getColumnIndex(MediaStore.MediaColumns.DATA)) val requestBody = File(picturePath).asRequestBody("image/jpeg".toMediaTypeOrNull()) return MultipartBody.Part.createFormData( "file", "${fileName}.jpeg", requestBody ) } } return null }
创建其他参数的代码
val userId = user.id.toRequestBody("text/plain".toMediaTypeOrNull()) val sessionId = session.id.toRequestBody("text/plain".toMediaTypeOrNull())
问题排查与解决方法
1. 废弃字段导致文件读取异常
Android 10(API 29)及以上版本中,MediaStore.MediaColumns.DATA字段已被废弃,通过这个字段获取的文件路径可能无效,导致你读取的不是目标图片,甚至误读取了更大的无关文件,最终触发服务器的大小限制。
解决代码:改用ContentResolver直接读取文件流,避免依赖废弃路径:
fun createImageRequestBody(imageUri: Uri, fileName: String): MultipartBody.Part? { context.contentResolver.openInputStream(imageUri)?.use { inputStream -> val tempFile = File(context.cacheDir, "${fileName}.jpeg").apply { inputStream.copyTo(outputStream()) } val requestBody = tempFile.asRequestBody("image/jpeg".toMediaTypeOrNull()) return MultipartBody.Part.createFormData( "file", "${fileName}.jpeg", requestBody ) } return null }
2. 未压缩图片导致体积过大
Postman中使用的图片可能经过压缩,而你的代码直接读取原始图片文件,体积远大于Postman发送的版本,触发服务器的大小阈值。
解决代码:添加图片压缩逻辑,将图片压缩到合适大小后再构建请求体:
// 图片压缩工具方法,可根据需求调整压缩质量 fun compressImage(uri: Uri, context: Context): File? { val bitmap = BitmapFactory.decodeStream(context.contentResolver.openInputStream(uri)) val outputStream = ByteArrayOutputStream() // 保留80%质量,可根据需要调整数值(0-100) bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream) val compressedFile = File(context.cacheDir, "compressed_${System.currentTimeMillis()}.jpg") compressedFile.outputStream().use { it.write(outputStream.toByteArray()) } bitmap.recycle() return compressedFile } // 修改后的createImageRequestBody fun createImageRequestBody(imageUri: Uri, fileName: String): MultipartBody.Part? { val compressedFile = compressImage(imageUri, context) ?: return null val requestBody = compressedFile.asRequestBody("image/jpeg".toMediaTypeOrNull()) return MultipartBody.Part.createFormData( "file", "${fileName}.jpeg", requestBody ) }
3. Multipart参数名称不匹配
检查Postman中的文件参数名:如果Postman里的文件参数是file1、file2、file3、file4,而你的代码中所有文件的参数名都是file,可能导致服务器解析异常,或误判请求数据大小。
解决方法:根据Postman的参数名调整createFormData的第一个参数:
// 对应四个文件分别设置参数名 // 第一个文件 MultipartBody.Part.createFormData("file", "${fileName1}.jpeg", requestBody1) // 第二个文件 MultipartBody.Part.createFormData("file2", "${fileName2}.jpeg", requestBody2) // 第三个文件 MultipartBody.Part.createFormData("file3", "${fileName3}.jpeg", requestBody3) // 第四个文件 MultipartBody.Part.createFormData("file4", "${fileName4}.jpeg", requestBody4)
4. OkHttp请求大小限制
检查OkHttpClient的配置,是否自定义了过小的请求大小限制(默认OkHttp无严格限制,但自定义配置可能出错)。如果有如下配置,调整为足够大的值或移除:
// 错误示例:限制请求体最大10MB,若实际请求超过则触发问题 val client = OkHttpClient.Builder() .maxRequestBodySize(10 * 1024 * 1024) .build()
内容的提问来源于stack exchange,提问作者RafaelFerreira
相关产品推荐
相关产品推荐

