在Golang中没有缓冲http.ResponseWritter

时间:2013-10-10 09:34:11

标签: http stream go flush

我正在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的输出也被刷新。无论如何要“自动刷新”ResponseWritter?

解决方案

我在golang的邮件列表中找到了帮助。有两种方法可以实现这一点:使用hijacker允许接管HTTP的底层TCP连接,或者在一个将写入和刷新的go例程中管道命令的stdout和stderr:

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

2 个答案:

答案 0 :(得分:26)

正如the documentation所暗示的那样,某些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")
}

请注意,在网络或客户端的许多其他地方都可以进行缓冲。

答案 1 :(得分:1)

很抱歉,如果我误解了你的问题,但下面会有类似的做法吗?

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&param2=value2" http://localhost:8080

返回:

  
    

参数1 =值1&安培; param2的=值2

  

你总是可以将你想要的任何数据附加到body,或者从其他地方读取更多的字节,然后再将其全部写出来。