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

如何使用io.Pipe获取exec.Command输出发起HTTP多表单POST请求

问题根因分析
    1. 流方向完全错误导致IO死锁:io.Pipe是单向通道,bodyWriter为写入端、bodyReader为读出端,你同时将cmd.Stdout和formWriter都绑定到bodyWriter,又在goroutine中从bodyReader读数据回写到part形成循环流,HTTP请求读取bodyReader时永远拿不到符合格式的数据流,直接卡住。
    1. 忽略http.NewRequest错误返回触发空指针panic:你用_丢弃了请求创建的错误,一旦请求初始化失败,req为nil,后续操作req.Header就会触发你遇到的SIGSEGV空指针崩溃。
    1. exec.Command参数语法错误:"-resize" "30x30"之间缺少逗号,两个参数需要分开传递,否则命令会直接执行失败。
    1. multipart写入顺序错误:multipart格式要求先创建字段part,再写入字段内容,最后关闭writer写入结束边界,原代码的执行顺序完全不符合要求。
修正后完整代码
func main() {
    // 修正命令参数错误,将-resize和30x30拆分为独立参数
    cmd := exec.Command("convert", "test.png", "-resize", "30x30", "png:-")
    bodyReader, bodyWriter := io.Pipe()
    // multipart writer绑定pipe写入端,所有表单内容直接写入pipe
    formWriter := multipart.NewWriter(bodyWriter)

    // 所有写入逻辑放到独立协程,避免和主协程的读操作死锁
    go func() {
        // 异常场景也必须关闭pipe写入端,否则HTTP侧会一直阻塞等待数据
        defer bodyWriter.Close()
        // 先创建form的file字段part
        partWriter, err := formWriter.CreateFormFile("file", "test_30x30.png")
        if err != nil {
            fmt.Println("form writer create error:", err)
            return
        }
        // 命令输出直接对接part写入端,不需要额外缓存
        cmd.Stdout = partWriter
        // 执行命令
        if err := cmd.Run(); err != nil {
            fmt.Println("command run error:", err)
            return
        }
        // 必须显式关闭formWriter,否则不会写入multipart结束边界,服务端会认为数据不完整
        _ = formWriter.Close()
    }()

    url := "http://example.com/upload"
    // 禁止忽略错误返回,避免空指针
    req, err := http.NewRequest(http.MethodPost, url, bodyReader)
    if err != nil {
        fmt.Println("create request error:", err)
        return
    }
    req.Header.Set("Content-Type", formWriter.FormDataContentType())
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("http do error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println(resp.StatusCode)
    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("read response error:", err)
        return
    }
    fmt.Println("Result:", string(respBody))
}
额外注意事项
  • io.Pipe的读写操作必须放在不同协程,否则会直接触发死锁
  • 所有可能返回错误的接口都不要忽略错误处理,避免出现无预兆的崩溃
  • 即使执行过程中出现异常,也要确保调用bodyWriter.Close(),否则HTTP请求侧会永久阻塞

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 17:06:04