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

Apache HttpClient5 Kotlin多部分请求编码字符集问题排查

Apache HttpClient 5.1.3 MultipartEntity 字节解析问题解决方案

问题描述

将HTTP REST客户端从Ktor切换至Apache HttpClient 5.1.3后,MultipartEntity处理二进制数据时出现异常:

  • Base64字符串解码为字节数组后,请求Payload中被错误插入DEL字符(0x7f),导致与Ktor/Postman发送的请求不一致
  • 日志对比:
    Ktor侧:[0xffffffc5]5[0x1d]
    HttpClient侧:[0xffffffc5][0x7f]5[0x1d]
  • 调用验证接口时返回200 OK,但响应体异常,错误内容如下:
{
"graph": [],
"input": {
"value": null
},
"report": {
"messages": [
{
"name": "DETECT_INPUT_TYPE",
"success": false,
"result": "<class 'TypeError'> ['TypeError: expected string or bytes-like object\n']",
"messageLevel": "ERROR"
}
],
"errorCount": 1,
"warningCount": 0,
"valid": false
}
}

当前构建多部分请求的代码:

inline fun <reified T> executeFormMultipartPost(
    url: String,
    bodyForm: ArrayList<NameValuePair>,
    headers: Map<String, MutableList<String>> = mapOf()
): T {
    val httpPost = HttpPost(url)
    for (header in headers) {
        header.value.forEach {
                value -> httpPost.addHeader(header.key, value)
        }
    }
    httpPost.addHeader("Accept-Charset", Charsets.UTF_8)

    var nvParams: ArrayList<NameValuePair> = ArrayList<NameValuePair>()
    for (n in bodyForm){
        nvParams.add(n)
    }

    var builder: MultipartEntityBuilder = MultipartEntityBuilder.create().setContentType(ContentType.create(
        "multipart/form-data"))
    builder.setMode(HttpMultipartMode.EXTENDED)
    builder.setMimeSubtype("form-data")
    val contentDisposition = nvParams[0].value
    val bytes = BaseEncoding.base64().decode(nvParams[1].value);

    val image = ByteArrayBody(bytes, ContentType.create(
        "multipart/form-data"),"badgeimage")
    val data = StringBody(nvParams[2].value, ContentType.create(
        "multipart/form-data"))
    builder.addPart(nvParams[1].name, image)
    builder.addPart(nvParams[2].name, data)

    val httpEntity = builder.build()
    httpPost.entity = httpEntity

    val response = client.execute(httpPost)
    return returnResponse(url, response)
}

解决方案

1. 修正二进制内容的ContentType设置

ByteArrayBody用于传递二进制文件(如图片),不能设置为multipart/form-data,需改为对应图片的MIME类型,比如image/png或image/jpeg,根据实际图片格式调整:

// 替换原ByteArrayBody创建代码
val image = ByteArrayBody(bytes, ContentType.create("image/png"), "badgeimage")

2. 清理MultipartEntityBuilder的错误配置

  • 移除setMimeSubtype("form-data"):MultipartEntityBuilder默认的子类型就是正确的,手动设置会破坏多部分请求的格式
  • 调整Multipart模式:如果EXTENDED模式导致解析异常,可尝试切换为BROWSER_COMPATIBLE模式,更贴近Postman的请求格式:
var builder = MultipartEntityBuilder.create()
    .setContentType(ContentType.MULTIPART_FORM_DATA)
    .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)

3. 修正Base64解码逻辑

确保Base64字符串没有被篡改,优先使用Java标准库的Base64解码器替代第三方库,避免解码差异:

// 替换原Base64解码代码
val bytes = Base64.getDecoder().decode(nvParams[1].value)

4. 移除冗余配置与代码

  • 删除Accept-Charset头:该头针对文本请求,二进制文件请求不需要此配置,可能干扰服务端解析
  • 移除冗余的参数复制逻辑:直接使用传入的bodyForm即可,无需重新创建nvParams

修改后的完整代码

inline fun <reified T> executeFormMultipartPost(
    url: String,
    bodyForm: ArrayList<NameValuePair>,
    headers: Map<String, MutableList<String>> = mapOf()
): T {
    val httpPost = HttpPost(url)
    // 处理请求头
    headers.forEach { (key, values) ->
        values.forEach { value ->
            httpPost.addHeader(key, value)
        }
    }

    // 构建多部分实体
    val builder = MultipartEntityBuilder.create()
        .setContentType(ContentType.MULTIPART_FORM_DATA)
        .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)

    // 解析Base64图片数据
    val imageBytes = Base64.getDecoder().decode(bodyForm[1].value)
    val imageBody = ByteArrayBody(imageBytes, ContentType.create("image/png"), "badgeimage")
    builder.addPart(bodyForm[1].name, imageBody)

    // 处理文本参数,指定UTF-8字符集
    val dataBody = StringBody(bodyForm[2].value, ContentType.TEXT_PLAIN.withCharset(Charsets.UTF_8))
    builder.addPart(bodyForm[2].name, dataBody)

    httpPost.entity = builder.build()
    val response = client.execute(httpPost)
    return returnResponse(url, response)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 20:10:49