Как обернуть exec.Command внутри io.Writer

Я пытаюсь сжать изображение в формате JPEG с помощью mozjpeg. Поскольку он не имеет официальной привязки, я просто вызову его 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?
}

Но не мог закончить это.

Кроме того, второй вопрос, когда я выключаю команду? Как отключить команду?

2 ответа

Вы должны взглянуть на 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,

Согласно ответу Кирилла, используйте cmd.StdInPipe передать данные, которые вы получаете Write,

Однако, с точки зрения закрытия, я бы соблазнился реализовать io.Closer. Это сделало бы *Compresser автоматически реализовать интерфейс io.WriteCloser.

я хотел бы использовать Close() как уведомление о том, что больше нет данных для отправки и что команда должна быть прервана. Любой ненулевой код завершения, возвращенный из команды, которая указывает на сбой, может быть перехвачен и возвращен как ошибка.

Я бы с осторожностью использовал CombinedOutput() внутри Write() в случае, если у вас медленный поток ввода. Утилита может завершить обработку входного потока и ожидать дополнительных данных. Это будет неправильно определено как завершение команды и приведет к неверному выводу.

Помните, что Write метод может вызываться неопределенное количество раз во время операций ввода-вывода.

Другие вопросы по тегам