在Golang中实时从Stdout获取数据



在我的程序中,我希望能够实时监视一些控制台命令的执行。

下面是两个代码示例,它们在控制台中实时显示数据,但在web页面中数据不是实时显示的,而是在命令完成后显示的。

请告诉我如何在网页中获得实时数据?

在这两个示例中:控制台实时显示数据。在web界面上,数据不是实时显示的,而是命令执行完成后显示的。我需要在一个网页上实时显示数据。

示例1

package main
import (
"bytes"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"io"
)
func main() {
http.HandleFunc("/", mainLogic)
http.ListenAndServe(":8080", nil)
}
func mainLogic(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("ping", "8.8.8.8", "-c5")
var stdBuffer bytes.Buffer
mw := io.MultiWriter(os.Stdout, &stdBuffer)
cmd.Stdout = mw
cmd.Stderr = mw
if err := cmd.Run(); err != nil {
log.Panic(err)
}
fmt.Fprintf(w, stdBuffer.String())
}

示例2

package main
import (
"fmt"
"log"
"net/http"
"os"
"os/exec"
"io"
"sync"
)
func main() {
http.HandleFunc("/", mainLogic)
http.ListenAndServe(":8080", nil)
}
func mainLogic(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("ping", "8.8.8.8", "-c5")
var stdout []byte
var errStdout, errStderr error
stdoutIn, _ := cmd.StdoutPipe()
stderrIn, _ := cmd.StderrPipe()
err := cmd.Start()
if err != nil {
log.Fatalf("cmd.Start() failed with '%s'n", err)
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
stdout, errStdout = copyAndCapture(os.Stdout, stdoutIn)
wg.Done()
}()
_, errStderr = copyAndCapture(os.Stderr, stderrIn)
wg.Wait()
err = cmd.Wait()
if err != nil {
log.Fatalf("cmd.Run() failed with %sn", err)
}
if errStdout != nil || errStderr != nil {
log.Fatal("failed to capture stdout or stderrn")
}
fmt.Fprintf(w, string(stdout))
}
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
var out []byte
buf := make([]byte, 1024, 1024)
for {
n, err := r.Read(buf[:])
if n > 0 {
d := buf[:n]
out = append(out, d...)
_, err := w.Write(d)
if err != nil {
return out, err
}
}
if err != nil {
if err == io.EOF {
err = nil
}
return out, err
}
}
}

由于性能原因,HTTP响应默认是缓冲的,因此您需要显式调用Flush以便立即发送数据。

如果您想从分配给exec.Cmd输出的写入器中执行此操作,您可以将http.ResponseWriter包装在一个类型中,该类型将在每个Write上为您调用Flush

type flushWriter interface {
io.Writer
http.Flusher
}
type flusher struct {
w flushWriter
}
func (f *flusher) Write(d []byte) (int, error) {
n, err := f.w.Write(d)
if err != nil {
return n, err
}
f.w.Flush()
return n, nil
}
...
if w, ok := w.(flushWriter); ok {
cmd.Stdout = &flusher{w}
cmd.Stderr = &flusher{w}
}

如果需要数据的副本,可以使用io.MultiWriter在写入输出时捕获输出。这里我们还讨论了http.ResponseWriter不是http.Flusher的情况,但这只有在您已经通过其他中间件封装的情况下才有可能发生。

var mw io.Writer
var buff bytes.Buffer
if w, ok := w.(flushWriter); ok {
mw = io.MultiWriter(&buff, &flusher{w})
} else {
mw = io.MultiWriter(&buff, w)
}
cmd.Stdout = mw
cmd.Stderr = mw

最新更新