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

Android Kotlin Retrofit POST多部分请求报400错误排查求助

排查Retrofit调用OpenAI图片变体接口400错误问题

我参考多篇教程在Android Kotlin中实现Retrofit的POST Multipart请求,目标调用OpenAI的v1/images/variations接口,请求体需要包含n、size参数以及图片文件,请求头要带Bearer Token。已经写好API接口、Service Builder和调用代码,修改过n和size的参数类型后还是返回Http 400 Bad Request,需要帮忙排查问题。

API接口代码

@Headers("Content-Type: multipart/form-data")
@POST("v1/images/variations")
@Multipart
suspend fun requestVariation(@Header("Authorization") token: String,
                             @Part("n") n: RequestBody,
                             @Part("size") size: RequestBody,
                             @Part image: MultipartBody.Part) :Call<ResponseClass>

Service Builder代码

object VariationBuilder {

    private val client = OkHttpClient.Builder().build()

    private val retrofit = Retrofit.Builder()
            .baseUrl("https://api.openai.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build()

    fun<T> buildService (service :Class<T>): T{
        return retrofit.create(service)
    }
}

MainActivity调用代码

val linearLayout: LinearLayout = findViewById(R.id.img_linear_layout)
val numberSpinner: Spinner = findViewById(R.id.spinner)
val tokenText: EditText = findViewById(R.id.token_box)

linearLayout.removeAllViews()
val newID = 1
view.id = newID
linearLayout.addView(view)

val cantSelec = numberSpinner.selectedItem.toString().toInt() - 1

val retrofit = VariationBuilder.buildService(RestApiInterface::class.java)

val dir = File(externalCacheDir,File.separator + "Variations")
dir.mkdirs()

val fileName = String.format("%d.jpg", System.currentTimeMillis())
val file: File = File(dir,File.separator + fileName)
file.createNewFile()

//将Bitmap转为字节数组
val bitmap: Bitmap? = createBitmapFromView(this@MainActivity,view)
val bos = ByteArrayOutputStream()
bitmap?.compress(Bitmap.CompressFormat.PNG, 0, bos) // 也可以保存为JPEG格式
val bitmapdata = bos.toByteArray()

//写入文件
val fos = FileOutputStream(file)
fos.write(bitmapdata)
fos.flush()
fos.close()

Toast.makeText(applicationContext, "请稍等,加载中...", Toast.LENGTH_LONG).show()

retrofit.requestVariation(
    "Bearer " + tokenText.text.toString(),
    RequestBody.create(MultipartBody.FORM,cantSelec.toString()),
    RequestBody.create(MultipartBody.FORM,"512x512"),
    image = MultipartBody.Part
        .createFormData(
            "image",
            fileName,
            RequestBody.create(MultipartBody.FORM, file)
        )
)

问题排查及修复方案

1. 移除手动添加的Content-Type头

使用@Multipart注解时,Retrofit会自动生成包含boundary参数的Content-Type: multipart/form-data头,手动添加的@Headers("Content-Type: multipart/form-data")会覆盖自动生成的头,缺失boundary导致服务器无法解析请求体。直接删除该注解:

@POST("v1/images/variations")
@Multipart
suspend fun requestVariation(@Header("Authorization") token: String,
                             @Part("n") n: RequestBody,
                             @Part("size") size: RequestBody,
                             @Part image: MultipartBody.Part) :Response<ResponseClass>

2. 修正suspend函数返回值

suspend函数不需要返回Call<ResponseClass>,直接返回Response<ResponseClass>或ResponseClass即可,否则会导致请求执行逻辑异常:

suspend fun requestVariation(@Header("Authorization") token: String,
                             @Part("n") n: RequestBody,
                             @Part("size") size: RequestBody,
                             @Part image: MultipartBody.Part) :Response<ResponseClass>

3. 确保在协程中调用suspend函数

suspend函数必须在协程作用域内执行,否则会抛出异常,导致请求未正常发送。修改调用代码,用lifecycleScope包裹:

lifecycleScope.launch {
    try {
        val response = retrofit.requestVariation(
            "Bearer " + tokenText.text.toString(),
            RequestBody.create(MultipartBody.FORM,cantSelec.toString()),
            RequestBody.create(MultipartBody.FORM,"512x512"),
            image = MultipartBody.Part.createFormData(
                "image",
                fileName,
                RequestBody.create(MediaType.parse("image/png"), file)
            )
        )
        if (!response.isSuccessful) {
            // 打印错误详情,方便定位问题
            Log.e("OpenAIErr", response.errorBody()?.string() ?: "未知错误")
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

4. 修正图片文件的MediaType

你将Bitmap压缩为PNG格式,但文件名是.jpg,且用MultipartBody.FORM作为文件的MediaType,这会导致服务器识别文件类型错误。需指定正确的MediaType:

val imageRequestBody = RequestBody.create(MediaType.parse("image/png"), file)
val imagePart = MultipartBody.Part.createFormData("image", fileName, imageRequestBody)

5. 修正n参数的取值范围

OpenAI的n参数允许范围是1-10,你的代码中cantSelec = numberSpinner.selectedItem.toString().toInt() - 1会导致取值为0(当Spinner选1时),不符合接口要求。去掉减1操作:

val cantSelec = numberSpinner.selectedItem.toString().toInt()

6. 添加日志拦截器排查请求细节

添加OkHttp日志拦截器,查看实际发送的请求头、请求体是否符合接口要求:

// 修改VariationBuilder中的client配置
private val client = OkHttpClient.Builder()
    .addInterceptor(HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BODY
    })
    .build()

同时添加依赖:

implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'

内容的提问来源于stack exchange,提问作者eLoco oMojogm

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 03:55:26