2

我正在尝试使用 mozjpeg 压缩 JPEG 图像。由于它没有官方的 go 绑定,我想我会调用它的 CLI 来进行压缩。

我尝试在以下情况下对用法进行建模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?
}

却没能完成。

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

4

2 回答 2

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.

于 2017-07-25T09:14:31.550 回答
0

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

但是,在关闭方面,我很想实施io.Closer。这将*Compresser自动实现io.WriteCloser接口。

我将Close()用作通知没有更多数据要发送并且应该终止命令。从命令返回的任何指示失败的非零退出代码都可以被捕获并作为错误返回。

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

请记住,Write在 IO 操作期间,该方法可以被无限次调用。

于 2017-07-25T09:26:08.723 回答