我正在用Go编写一个简单的Web应用程序,我希望我的响应被流式传输到客户端(即一旦请求被完全处理,就不会缓冲并分块发送(:
func handle(res http.ResponseWriter, req *http.Request) {
fmt.Fprintf(res, "sending first line of data")
sleep(10) //not real code
fmt.Fprintf(res, "sending second line of data")
}
从客户的角度来看,两条线将同时发送。任何建议都值得赞赏:)
回答后编辑@dystroy
我可以在我个人进行的每次写入后刷新,但在我的用例中,这还不够:
cmd := exec.Command("a long command that outputs lots of lines")
cmd.Stdout = res //where res is a http.ResponseWritter
cmd.Stderr = res
err := cmd.Run()
我也希望刷新cmd
的输出。无论如何要"自动刷新"响应写入器?
溶液
我在golang的邮件列表中找到了帮助。有两种方法可以实现这一点:使用允许接管HTTP底层TCP连接的劫机者,或者在将写入和刷新的go例程中管道命令的stdout和stdrr:
pipeReader, pipeWriter := io.Pipe()
cmd.Stdout = pipeWriter
cmd.Stderr = pipeWriter
go writeCmdOutput(res, pipeReader)
err := cmd.Run()
pipeWriter.Close()
//---------------------
func writeCmdOutput(res http.ResponseWriter, pipeReader *io.PipeReader) {
buffer := make([]byte, BUF_LEN)
for {
n, err := pipeReader.Read(buffer)
if err != nil {
pipeReader.Close()
break
}
data := buffer[0:n]
res.Write(data)
if f, ok := res.(http.Flusher); ok {
f.Flush()
}
//reset buffer
for i := 0; i < n; i++ {
buffer[i] = 0
}
}
}
上次更新
更漂亮:http://play.golang.org/p/PpbPyXbtEs
如文档中所暗示的,某些ResponseWriter
可能会实现Flusher
接口。
这意味着您可以执行以下操作:
func handle(res http.ResponseWriter, req *http.Request) {
fmt.Fprintf(res, "sending first line of data")
if f, ok := res.(http.Flusher); ok {
f.Flush()
} else {
log.Println("Damn, no flush");
}
sleep(10) //not real code
fmt.Fprintf(res, "sending second line of data")
}
请注意,缓冲可能发生在网络或客户端的许多其他位置。
抱歉,如果我误解了您的问题,但是下面这样的东西可以解决问题吗?
package main
import (
"bytes"
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
body := make([]byte, int(r.ContentLength))
b := bytes.NewBuffer(body)
if _, err := b.ReadFrom(r.Body); err != nil {
fmt.Fprintf(w, "%s", err)
}
if _, err := b.WriteTo(w); err != nil {
fmt.Fprintf(w, "%s", err)
}
}
func main() {
http.HandleFunc("/", handler)
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
$ curl --data "param1=value1¶m2=value2" http://localhost:8080
返回:
参数1=值1&参数2=值2
您可以随时附加任何要body
的数据,或者在再次将其全部写出之前从其他地方将更多字节读取到缓冲区中。
在 Go 1.20 或更高版本中,使用 ResponseController.Flush 刷新响应。
func handle(res http.ResponseWriter, req *http.Request) {
rc := http.NewResponseController(res)
fmt.Fprintf(res, "sending first line of data")
if err := rc.Flush(); err != nil {
// Handle error in some way.
log.Println("Oops, no flush")
}
sleep(10) //not real code
fmt.Fprintf(res, "sending second line of data")
}