如何使用Golang io Reader读取数据_Golang数据流读取方式

io.Reader 是只读、单向、不可重放的接口,需由具体类型实现;读取全部内容优先用 io.ReadAll,手动循环须同时检查 n 和 err;HTTP Body 读完需 Close,多次读取需缓存。

io.Reader 是接口,不是具体类型

Go 里 io.Reader 是一个接口,定义了 Read([]byte) (int, error) 方法。它不负责管理缓冲、不自动重试、也不区分数据来源——文件、网络连接、字符串、bytes.Buffer 都可以实现它。你不能直接 “创建一个 io.Reader”,而是拿到一个已经实现了它的值,比如 os.Filebytes.NewReader()http.Response.Body

常见错误是试图对 io.Reader 做类型断言或初始化,比如写 var r io.Reader = new(io.Reader) —— 这会编译失败,因为接口不能直接实例化。

读取全部内容用 io.ReadAll,别手动循环

如果目标是把整个流读完(比如小配置文件、API 响应体),优先用 io.ReadAll,而不是自己写 for 循环调 Read。它内部做了扩容和边界检查,比手写更安全。

data, err := io.ReadAll(r)
if err != nil {
    log.Fatal(err)
}
// data 是 []byte,可转 string(data)

注意:io.ReadAll 会一次性把所有数据加载进内存,不适合处理 GB 级流;此时应改用 io.Copy 或分块读取。

  • Go 1.16+ 才有 io.ReadAll;旧版本用 ioutil.ReadAll(已弃用)
  • 如果 r 来自 http.Response.Body,记得在读完后 Close(),否则连接不会复用

分块读取要检查返回的 n 和 err

手动循环读取时,必须同时检查 n(实际读取字节数)和 err。常见误区是只判断 err != nil 就退出,但 io.EOF 是正常结束信号,而 n == 0 && err == nil 可能是空流或阻塞中(如管道未写入)。

buf := make([]byte, 4096)
for {
 

n, err := r.Read(buf) if n > 0 { // 处理 buf[:n] } if err == io.EOF { break } if err != nil { log.Fatal(err) } }
  • 每次 Read 不保证填满 buffer,n 可能小于 len(buf)
  • 不要假设 Read 会一次读完一行或一个 JSON 对象;需要按协议解析时,应包装成 bufio.Scannerjson.Decoder

别忽略 Reader 的状态不可逆性

io.Reader 是单向、不可重放的。一旦读过,数据就“流走”了。比如从 http.Response.Body 读了一次,再读就是空;bytes.Reader 虽然支持 Seek,但普通 io.Reader 接口本身不提供重置能力。

如果需要多次读取,常见做法是:

  • bytes.NewReader(data) 把已读内容转成新 Reader
  • io.TeeReader 在读的同时写入 bytes.Buffer 缓存
  • 对 HTTP Body,用 resp.Body = io.NopCloser(bytes.NewReader(cached)) 替换原 Body

最容易被忽略的是:HTTP 客户端默认不缓存 Body,出错重试时若没提前保存,就再也拿不到原始响应流了。