package iio import ( "io" "io/ioutil" "os" "sync" ) type DiskTeeReader struct { io.ReaderAt io.Closer io.Reader m sync.Mutex fo int64 fr *os.File to int64 tr io.Reader } func NewDiskTeeReader(r io.Reader) (Reader, error) { fr, err := ioutil.TempFile("", "dtb_tmp") if err != nil { return nil, err } tr := io.TeeReader(r, fr) return &DiskTeeReader{fr: fr, tr: tr}, nil } func (dtr *DiskTeeReader) ReadAt(p []byte, off int64) (int, error) { dtr.m.Lock() defer dtr.m.Unlock() tb := off + int64(len(p)) if tb > dtr.fo { w, err := io.CopyN(ioutil.Discard, dtr.tr, tb-dtr.fo) dtr.to += w if err != nil && err != io.EOF { return 0, err } } n, err := dtr.fr.ReadAt(p, off) dtr.fo += int64(n) return n, err } func (dtr *DiskTeeReader) Read(p []byte) (n int, err error) { dtr.m.Lock() defer dtr.m.Unlock() // use directly tee reader here n, err = dtr.tr.Read(p) dtr.to += int64(n) return } func (dtr *DiskTeeReader) Close() error { if err := dtr.fr.Close(); err != nil { return err } return os.Remove(dtr.fr.Name()) }