【问题标题】:Using io.Pipe to get output from exec.command to make http post使用 io.Pipe 从 exec.command 获取输出以进行 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()

【问题讨论】:

  • 程序中哪一行代码出现了恐慌?

标签: go pipe reader


【解决方案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 函数中获取大小?
    • 你可以关注这个帖子stackoverflow.com/questions/20945069/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    • 2021-02-03
    相关资源
    最近更新 更多