如何在io.Writer中包装exec.Command

时间:2017-07-25 09:09:10

标签: image-processing go mozjpeg

我尝试使用mozjpeg压缩JPEG图像。由于它没有正式的绑定,我想我只是调用它来进行压缩。

我尝试在compress/gzip之后对用法进行建模:

c := jpeg.NewCompresser(destFile)
_, err := io.Copy(c, srcFile)

现在的问题是,如何将CLI包装在Compresser中,以便它可以支持这种用法?

我试过这样的事情:

type Compresser struct {
    cmd exec.Command
}

func NewCompressor(w io.Writer) *Compresser {
    cmd := exec.Command("jpegtran", "-copy", "none")
    cmd.Stdout = w
    c := &Compresser{cmd}
    return c
}

func (c *Compresser) Write(p []byte) (n int, err error) {
    if c.cmd.Process == nil {
        err = c.cmd.Start()
        if err != nil {
            return
        }
    }
    // How do I write p into c.cmd.Stdin?
}

但无法完成它。

另外,第二个问题是,我什么时候关闭命令?如何关闭命令?

2 个答案:

答案 0 :(得分:1)

你应该看一下Cmd.StdinPipe。文档中有一个例子适合您的情况:

package main

import (
    "fmt"
    "io"
    "log"
    "os/exec"
)

func main() {
    cmd := exec.Command("cat")
    stdin, err := cmd.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }

    go func() {
        defer stdin.Close()
        io.WriteString(stdin, "values written to stdin are passed to cmd's standard input")
    }()

    out, err := cmd.CombinedOutput()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%s\n", out)
}

在这种情况下,CombinedOutput()执行您的命令,并且当没有更多字节可以从out读取时执行完成。

答案 1 :(得分:0)

根据Kiril的回答,使用cmd.StdInPipe将您收到的数据传递给Write

然而,就结束而言,我很想实施io.Closer。这将使*Compresser自动实现io.WriteCloser接口。

我会使用Close()作为通知,告知不再有数据要发送,并且命令应该终止。从命令返回的任何非零退出代码都可以捕获并作为错误返回。

如果您的输入流速度较慢,我会谨慎使用CombinedOutput() Write()。该实用程序可以完成处理输入流并等待更多数据。这将被错误地检测为命令完成,并将导致无效输出。

请记住,在IO操作期间,Write方法可以被称为不确定的次数。