Go 里面这样可以实现一个允许可重复读、多次副本读的 io.Reader,还挺好玩的,感觉又在 #套娃……

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
	"bytes"
	"io"
	"os"
	"strings"
)

// 基于内存实现的可重复读的 Reader。
func MemDupReader(r io.Reader) func() io.Reader {
	b := bytes.NewBuffer(nil)
	t := io.TeeReader(r, b)

	return func() io.Reader {
		br := bytes.NewReader(b.Bytes())
		return io.MultiReader(br, t)
	}
}

func main() {
	r := strings.NewReader("123\n")

	dup := MemDupReader(r)

	for i := 0; i < 10; i++ {
		io.Copy(os.Stdout, dup())
	}
}

结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
tests (main) → go run main.go
123
123
123
123
123
123
123
123
123
123

主要原理是:

  1. 通过 TeeReader 把从原始接口读出来的数据顺便写一份拷贝到内存 Buffer 里面(io.Writer);
  2. 然后下次读的时候,首先从内存读(io.Reader),然后继续从原始的数据读;
  3. 通过内存 BufferBytes() 重建一个 bytes.Reader 支持了 io.Seeker1

由于是在内存中缓存的,所以要小心数据不能太大。否则应该用文件实现缓冲(io.ReadWriteSeeker)。


  1. 奇怪为什么 bytes.Buffer 不支持 Seek? ^

桃子的碎碎念 桃子 编辑