How to wrap exec.Command inside an io.Writer

huangapple go评论90阅读模式
英文:

How to wrap exec.Command inside an io.Writer

问题

我正在尝试使用mozjpeg在Go语言中压缩JPEG图像。由于它没有官方的Go绑定,我打算调用其命令行界面来进行压缩。

我试图模仿compress/gzip的用法:

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

现在的问题是,我该如何将命令行界面包装在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
        }
    }
    // 我该如何将p写入c.cmd.Stdin?
}

但是我无法完成它。

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

英文:

I'm trying to compress a JPEG image in go using mozjpeg. Since it doesn't have official go binding, I think I'll just invoke its CLI to do the compression.

I try to model the usage after compress/gzip:

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

Now the question is, how do I wrap the CLI inside Compresser so it can support this usage?

I tried something like this:

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?
}

But couldn't finish it.

Also, a second question is, when do I shut down the command? How to shut down the command?

答案1

得分: 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 中没有更多的字节可读时,执行就完成了。

英文:

You should take a look at the Cmd.StdinPipe. There is an example in the documentation, which suits your case:

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)
}

In this case, CombinedOutput() executes your command, and the execution is finished, when there are no more bytes to read from out.

答案2

得分: 0

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

然而,在关闭方面,我倾向于实现io.Closer接口。这将使*Compresser自动实现io.WriteCloser接口。

我会使用Close()方法来通知没有更多数据需要发送,并且应该终止命令。如果命令返回非零的退出代码表示失败,可以捕获并作为错误返回。

Write()方法中使用CombinedOutput()时要小心,以防输入流较慢。工具可能已经完成处理输入流,并正在等待更多数据。这将被错误地检测为命令完成,并导致无效的输出。

请记住,在IO操作期间,Write方法可以被调用多次,次数不确定。

英文:

As per Kiril's answer, use the cmd.StdInPipe to pass on the data you receive to Write.

However, in terms of closing, I'd be tempted to implement io.Closer. This would make *Compresser automatically implement the io.WriteCloser interface.

I would use Close() as the notification that there is no more data to be sent and that the command should be terminated. Any non-zero exit code returned from the command that indicates failure could be caught and returned as an error.

I would be wary of using CombinedOutput() inside Write() in case you have a slow input stream. The utility could finish processing the input stream and be waiting for more data. This would be incorrectly detected as command completion and would result in an invalid output.

Remember, the Write method can be called an indeterminate number of times during IO operations.

huangapple
  • 本文由 发表于 2017年7月25日 17:09:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/45298749.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定