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

Ktor图片上传失败:submitFormWithBinaryData报Unknown form content type错误

Ktor 图片上传返回400 Bad Request问题解决

问题概述

使用Ktor的submitFormWithBinaryData API上传图片至服务器时,返回400 Bad Request错误,服务器日志提示:Unknown form content type: io.ktor.http.content.PartData$FileItem@334102c。

问题根源

  1. 手动构建PartData.FileItem导致解析异常:直接将自定义创建的PartData.FileItem对象传入formData构建器,Ktor无法正确处理该对象的序列化逻辑,导致服务器收到的表单数据格式异常。
  2. 手动设置Content-Type丢失边界信息:手动指定Content-Type: multipart/form-data时未包含自动生成的boundary参数,服务器无法正确分割多部分表单的各个字段,进而无法识别文件的内容类型。

修复方案

1. 使用Ktor内置的文件添加方式

移除自定义的asMultipartPart函数,直接使用formData构建器的append重载方法添加文件,该方法会自动正确构建文件对应的表单部分。

2. 移除手动设置的multipart/form-data头

submitFormWithBinaryData会自动生成包含boundary的Content-Type头(格式为multipart/form-data; boundary=xxx),手动设置会覆盖这个正确的头信息,导致服务器解析失败。

修改后的代码

suspend fun updateProfileImage(endPoint: String, imageFile: File): UpdateProfileImageResponse? {
    if (!imageFile.exists() || imageFile.length() == 0L) {
        Log.e("TAG", "Image file does not exist or is empty")
        return null
    }
    return try {
        val curlHeaders = mutableListOf<String>()
        val response: HttpResponse = client.submitFormWithBinaryData(
            url = "https://$endPoint",
            formData = formData {
                // 使用Ktor内置方法添加文件,自动处理PartData逻辑
                append("image", imageFile.readBytes(), Headers.build {
                    append(HttpHeaders.ContentDisposition, "form-data; name=\"image\"; filename=\"${imageFile.name}\"")
                    append(HttpHeaders.ContentType, "image/jpeg")
                })
            }
        ) {
            headers {
                val headersMap = mutableMapOf<String, String>()
                appendRequestHeaders(headersMap, isImageAPI = true)
                // 移除手动设置的Content-Type,由框架自动生成带boundary的正确头
                headersMap.forEach { (key, value) -> 
                    append(key, value) 
                    curlHeaders.add("-H \"$key: $value\"")
                }
            }
        }

        if (response.status.isSuccess()) {
            val responseBody = response.body<String>()
            json.decodeFromString<UpdateProfileImageResponse>(responseBody)
        } else {
            null
        }
    } catch (e: Exception) {
        e.message?.logE("uploadProfileImage")
        null
    }
}

额外优化

如果需要支持多图片格式(如PNG),可以通过文件扩展名动态设置ContentType:

val contentType = when(imageFile.extension.lowercase()) {
    "jpg", "jpeg" -> ContentType.Image.JPEG
    "png" -> ContentType.Image.PNG
    else -> ContentType.Application.OctetStream
}

将上述contentType代入append方法的Headers构建中即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:03:20