It's inefficient if we write to a file one character or word at a time. The write to file works best when done in blocks or chunks.
// Write writes the contents of p into the buffer.
// It returns the number of bytes written.
// If nn < len(p), it also returns an error explaining
// why the write is short.
func (b *Writer) Write(p []byte) (nn int, err error) {
for len(p) > b.Available() && b.err == nil {
var n int
if b.Buffered() == 0 {
// Large write, empty buffer.
// Write directly from p to avoid copy.
n, b.err = b.wr.Write(p)
} else {
n = copy(b.buf[b.n:], p)
b.n += n
b.Flush()
}
nn += n
p = p[n:]
}
if b.err != nil {
return nn, b.err
}
n := copy(b.buf[b.n:], p)
b.n += n
nn += n
return nn, nil
}
b.wr 是在 newBufioWriterSize 的时候传进去的
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
...
c.r = &connReader{conn: c}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
...
}
gin分片传输实现代码
package main
import (
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/test_stream", func(c *gin.Context) {
w := c.Writer
header := w.Header()
header.Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`
<html>
<body>
`))
w.Flush()
// 这里对每次循环输出都进行Flush刷新输出
for i := 0; i < 10; i++ {
w.Write([]byte(fmt.Sprintf(`
<h3>%d</h3>
`, i)))
//w.Flush()
w.Flush()
time.Sleep(time.Duration(1) * time.Second)
}
w.Write([]byte(`
</body>
</html>
`))
w.Flush()
})
r.Run("127.0.0.1:8080")
}
分块传输的基础:http的 transfer-encoding:chunked 协议
分块传输的编码规则如下: 1)每个分块包含两个部分,<长度头>和&<数据块>
当输出内容太大时,就可以使用分块传输的方式。分块传输是基于http的Transfer-Encoding: chunked协议进行的