使用 io.Pipe 从 exec.command 获取输出以进行 http post

Posted

技术标签:

【中文标题】使用 io.Pipe 从 exec.command 获取输出以进行 http post【英文标题】:Using io.Pipe to get output from exec.command to make http post 【发布时间】:2021-11-29 09:18:15 【问题描述】:

正在阅读使用 io.Pipe 来减少分配(无需将 cmd.Stdout 分配给 bytes.Buffer)。如果我非常了解如何使用 io.Pipe 从 exec.command 获取输出以发出 http mutliform 发布请求,我无法让以下工作和感激。

func main() 
    cmd := exec.Command("convert", "test.png", "-resize" "30x30", "png:-")
    bodyReader, bodyWriter := io.Pipe()
    cmd.Stdout = bodyWriter
    formWriter := multipart.NewWriter(bodyWriter)
    go func() 
        cmd.Run()
        partWriter, err := formWriter.CreateFormFile("file", "file")
        if err != nil 
            fmt.Println("form writer create error")
            return
        
        _, err = io.Copy(partWriter, bodyReader)
        if err != nil 
            fmt.Println("io copy error")
            return
        
        formWriter.Close()
        bodyWriter.Close()
    ()

    url := "http://example.com/upload"
    req, _ := http.NewRequest(http.MethodPost, url, bodyReader)
    req.Header.Set("Content-Type", formWriter.FormDataContentType())
    resp, err := http.DefaultClient.Do(req)
    if err != nil 
        fmt.Println("htttp do error")
        return
    
    fmt.Println(resp.StatusCode)
    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil 
        fmt.Println("read error")
        return
    
    fmt.Println("Result:", string(respBody))

使用调试工具,它只是挂在resp, err := http.DefaultClient.Do(req),最终输出如下。

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x1218c3b]

goroutine 1 [running]:
main.main()

【问题讨论】:

程序中哪一行代码出现了恐慌? 【参考方案1】:

通过将命令的标准输出设置为部分编写器来修复。

func main() 
    bodyReader, bodyWriter := io.Pipe()
    formWriter := multipart.NewWriter(bodyWriter)
    go func() (err error) 
        defer func() 
            bodyWriter.CloseWithError(err)
        ()
    
        partWriter, err := formWriter.CreateFormFile("file", "file")
        if err != nil 
            return err
        
    
        cmd := exec.Command("convert", "test.png", "-resize" "30x30", "png:-")
        cmd.Stdout = partWriter
    
        if err := cmd.Run(); err != nil 
            return err
        
    
        formWriter.Close()
        return nil
    ()

    ...


Run an example on the playground.

额外改进:使用 CloseWithError 将错误从 goroutine 传播到主函数。

【讨论】:

【参考方案2】:

您需要为multipart.Writer添加另一个Pipe

func main() 
    cmd := exec.Command("convert", "./test.jpg", "-resize", "30x30", "jpg:-")
    bodyReader, bodyWriter := io.Pipe()
    cmd.Stdout = bodyWriter
    cmd.Stderr = os.Stderr
    formReader, formWriter := io.Pipe()
    multiWriter := multipart.NewWriter(formWriter)

    go func() 
        if err := cmd.Run(); err != nil 
            bodyWriter.CloseWithError(err)
         else 
            bodyWriter.Close()
        
    ()

    go func() 
        partWriter, err := multiWriter.CreateFormFile("file", "file")
        if err != nil 
            formWriter.CloseWithError(err)
            return
        

        _, err = io.Copy(partWriter, bodyReader)
        if err != nil 
            formWriter.CloseWithError(err)
            return
        

        multiWriter.Close()
        formWriter.Close()
    ()

    url := "http://example.com/upload"
    req, _ := http.NewRequest(http.MethodPost, url, formReader)
    fmt.Println(multiWriter.FormDataContentType())
    req.Header.Set("Content-Type", multiWriter.FormDataContentType())
    resp, err := http.DefaultClient.Do(req)
    if err != nil 
        fmt.Println("htttp do error")
        return
    
    fmt.Println(resp.StatusCode)
    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil 
        fmt.Println("read error")
        return
    
    fmt.Println("Result:", string(respBody))

【讨论】:

工作...谢谢... 请提出另一个后续问题。如果 linesize, err = io.Copy(partWriter, bodyReader) 已创建,我如何在 main 函数中获取大小? 你可以关注这个帖子***.com/questions/20945069/…

以上是关于使用 io.Pipe 从 exec.command 获取输出以进行 http post的主要内容,如果未能解决你的问题,请参考以下文章

exec.Command调用java cli

exec.Command错误,没有输出到stdout或stderr

防止 Ctrl+C 中断 Golang 中的 exec.Command

在 Golang 中运行 exec.Command 时如何调试“退出状态 1”错误

带有 exec.Command 的 mysqldump 导致 1045 错误,但在终端上工作正常

如何在Golang中运行exec.Command时调试“exit status 1”错误