wuyongxiu / wuyongxiu.github.io

随便记录一下......
http://wuyongxiu.github.io
6 stars 3 forks source link

[golang] io.Reader,io.ReadCloser和io.ReadSeeker #15

Open wuyongxiu opened 7 years ago

wuyongxiu commented 7 years ago


在 io 包里有三个接口时常会相互转换 io.Reader io.ReadCloser 和 io.ReadSeeker

Reader接口封装了基本的 Read 方法,Read 方法读取长度为 len(p)的字节到 p 中,它返回读取到的字节长度(0<=n<=len(p))和遇到的任何错误。

type Reader interface{
   Read( p []byte) (n int, err error)
}


ReadCloser 接口封装了Reader和Closer接口,所以它具有基本的Read和Close方法。

type ReadCloser interface{
   Reader
   Closer
}


ReadSeeker 接口封装了基本的Read和Seek方法,Seek方法可以设定下一次读写的位移。

type ReadSeeker interface{
     Reader
     Seeker
}



1. 如何将 Reader 转化成 ReadCloser


go语言包 ioutil 提供了NopCloser方法可以将 io.Reader 封装为 io.ReadCloser
func NopCloser (r io.Reader) io.ReadCloser

参考go语言源码包(net/http),里面有很多示例,将请求或回复Body读取出来后,再利用bytes.NewReader(...)将bytes封装成 io.Reader,之后利用 ioutil.NopCloser 将io.Reader封装成 io.ReadCloser 接口

clipboard

2. 如何将Reader 或者 ReadCloser 转化为 ReadSeeker

这里有详细的原因和方法
http://stackoverflow.com/questions/37718191/how-do-i-go-from-io-readcloser-to-io-readseeker

原因: ReadSeeker 封装了Seek()方法,这个方法要求资源的任何位置都能被定位,例如存储在磁盘里文件,你可以随时读取文件的任意位置。而response.Body 是通过TCP连接从网络中读取数据,这些数据没有被存储,并且数据发送者不会再次发送数据给你,因此 response.Body 没有实现 io.Seeker 方法。

方法:基于以上分析,对于一些像 response.Body类型的 io.ReadCloser,将它转化为 ReadSeeker的方法就是先将 io.ReadCloser 全部读取到内存中,利用 ioutil.ReadAll() 方法,然后利用 bytes.NewReader() 方法就可以从[]byte中获得 io.ReadSeeker
它的缺点就是所有的内容都需要存储在内存中,这样会造成内存损耗。

renzhengeek commented 4 years ago

美女请教个问题,什么情况下,需要ReadCloser呢,为什么会有这种变体?

wuyongxiu commented 4 years ago

在http响应body读取后是需要close的,这种情况就需要ReadCloser。至于为什么需要ReadCloser,可以参考:what-could-happen-if-i-dont-close-response-body-in-golang