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

HttpUrlConnection多部分请求适配Multer报400错误排查

适配Multer的Multipart请求调整方案

我在应用中使用multipart request向服务器发送多文件及参数,此前适配express-formidable可正常运行,但切换为Multer后端库后功能失效。后端开发人员反馈接收的请求体为空,我收到400 - Bad Request响应。以下是需要调整的内容:

原请求代码

fun apiMultipart(): ApiResult {
    val url = URL(apiUrl)
    boundary = getBoundary()

    try {

        val connnection = createConnection(ApiMethod.POST.method, url)

        val requestStream = DataOutputStream(connnection.outputStream)
        formFieldData.data.forEach { ffd->
            addFormField(ffd.first, ffd.second?:"null", requestStream)
        }
        addBitmapData(data, requestStream)
        addEnd(requestStream)
        requestStream.flush()

        return getResponse(connnection)
    }catch (e: IOException){
        //return error - exception
    }
}

fun getResponse(con: HttpURLConnection): ApiResult{
    //handle response
}

private const val LINE_FEED = "\r\n"
private fun getBoundary(): String{
    return "===" + System.currentTimeMillis() + "==="
}

private fun createConnection(method: String, url: URL): HttpURLConnection{
    with(url.openConnection() as HttpURLConnection){
        requestMethod = method
        setRequestProperty(HEADER_CONTENT_TYPE, "multipart/form-data;boundary=$boundary")
        addRequestProperty(HEADER_API_KEY, apiDef.apiKey)
        session?.let { addRequestProperty(HEADER_SESSION, it) }
        setRequestProperty("Accept", "*/*")
        setRequestProperty("Connection", "keep-alive")
        setRequestProperty("cache-control", "no-cache")
        doOutput = true

        return this
    }
}

/**
 * Adds a form field to the request
 *
 * @param name  field name
 * @param value field value
 * @param outputStream data stream
 */
fun addFormField(name: String, value: Any, outputStream: DataOutputStream) {
    App.log("addFormFieldData: name: $name, value: $value")
    outputStream.apply {
        writeBytes("--$boundary$LINE_FEED")
        writeBytes("Content-Type: text/plain; charset=UTF-8;$LINE_FEED")
        writeBytes("Content-Disposition: form-data; name=\"$name\"$LINE_FEED$LINE_FEED")

        if (value is String){
            write(value.toByteArray(StandardCharsets.UTF_8))
        } else {
            writeBytes("$value")
        }
        writeBytes(LINE_FEED)
    }
}

fun addEnd(outputStream: DataOutputStream){
    outputStream.apply {
        writeBytes(LINE_FEED)
        writeBytes("--$boundary$LINE_FEED")
    }
}

fun addFileEnd(outputStream: DataOutputStream){
    outputStream.writeBytes(LINE_FEED)
}

fun addBitmapData(bmapsData: List<BitmapImageData>, outputStream: DataOutputStream){
    bmapsData.forEach { bmData ->
        addFileHeader(bmData.name, bmData.name, "image/jpeg", outputStream)
        bmData.bm.compress(Bitmap.CompressFormat.JPEG, 60, outputStream)
        addFileEnd(outputStream)
    }
}

fun addFileHeader(name: String, filename: String, contentType: String, outputStream: DataOutputStream){
    outputStream.apply {
        writeBytes("--$boundary$LINE_FEED")
        writeBytes("Content-Disposition: form-data; name=\"$name\"; filename=\"./$filename\"$LINE_FEED")
        writeBytes("Content-Type: $contentType$LINE_FEED$LINE_FEED")
    }
}

具体调整点

  • 修正请求结束边界格式:Multer要求multipart请求的结束边界必须是--boundary--(前后各加两个短横线),原代码仅用了--boundary,导致请求未正确闭合。修改addEnd方法:

    fun addEnd(outputStream: DataOutputStream){
        outputStream.apply {
            writeBytes("--$boundary--$LINE_FEED") // 末尾添加--完成闭合
        }
    }
    

    同时移除原方法里多余的LINE_FEED,避免出现无效空行。

  • 移除表单字段的冗余Content-Type头:普通表单字段不需要额外指定text/plain头,Multer对冗余头的兼容性较差。修改addFormField方法,删除这一行:

    writeBytes("Content-Type: text/plain; charset=UTF-8;$LINE_FEED")
    
  • 修正文件名格式:原代码中文件名使用./$filename的相对路径格式,Multer可能无法正确解析,直接使用文件名即可:

    // 修改addFileHeader中的这一行
    writeBytes("Content-Disposition: form-data; name=\"$name\"; filename=\"$filename\"$LINE_FEED")
    
  • 规范换行符使用:确保所有字段、文件块之间的分隔严格使用\r\n,避免出现多余空行。比如检查addFormField中写完字段值后的LINE_FEED,以及addFileEnd的换行是否必要,保证每个块的结构清晰。

调整原因

Multer对multipart请求的格式校验比express-formidable更严格,边界闭合格式、冗余头信息、文件名格式这些细节在formidable中可以兼容,但在Multer中会被判定为无效请求,直接返回400错误。调整后请求将符合Multer的解析要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 04:38:08