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

Golang使用multipart/form-data仅关闭一次就永久挂起问题咨询

问题原因分析

这个现象的核心原因和mime/multipart.Writer的实现逻辑、HTTP请求的构造顺序直接相关:

  • 首先需要确认实际代码中multiPartWriter.Close()的调用位置是否和示例一致:
    • 如果你是先调用http.NewRequest,再调用Close(),就会出现永久挂起的问题:
      • http.NewRequest接收bytes.Buffer作为请求体时,会当场读取Buffer的长度作为请求的Content-Length头的值
      • 此时还没调用Close(),Buffer是空的,Content-Length被设为0
      • 之后调用Close()会向Buffer写入multipart结束边界符,Buffer长度增加,但Content-Length已经不会更新了
      • 发送请求时,服务端收到Content-Length: 0和multipart/form-data的内容类型,但没收到任何multipart数据,会一直等待后续数据传输,导致请求挂起
    • 你所说的「调用两次Close就能正常运行」本质上是调用位置的巧合:如果第一次Close()是在http.NewRequest之前,第二次在之后,那第一次Close()已经把结束边界符写入了Buffer,http.NewRequest能读到正确的长度,自然不会挂起,而标准库的multipart.Writer.Close()是幂等实现,内置了closed标识,第二次调用不会做任何操作,完全不影响结果
  • 额外补充:你提供的示例代码没有处理client.Do返回的响应体,按照标准库要求,无论请求是否成功,都需要调用resp.Body.Close()释放连接,否则可能会出现连接泄漏的问题,未设置客户端超时也可能在网络异常时出现假挂起的情况。

修复方案

把multiPartWriter.Close()固定放在http.NewRequest调用之前即可,不需要多次调用,正确写法如下:

func main() {
    var requestBody bytes.Buffer
    multiPartWriter := multipart.NewWriter(&requestBody)
    // 必须在构造请求之前关闭,保证buffer内容完整,Content-Length计算正确
    if err := multiPartWriter.Close(); err != nil {
        // 错误处理逻辑
        panic(err)
    }
    req, err := http.NewRequest("POST", "https://api.telegram.org/bot<telegram token>/getme", &requestBody)
    if err != nil {
        panic(err)
    }
    req.Header.Set("Content-Type", multiPartWriter.FormDataContentType())
    // 建议设置超时,避免网络问题导致永久挂起
    client := &http.Client{
        Timeout: 10 * time.Second,
    }
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    // 必须关闭响应体释放连接
    defer resp.Body.Close()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 14:54:04