I am playing around Golang. About io.Copy I put 2 consecutive io.Copy in the code, but i expect it output twice result(testtesttest). But the 2nd one is nil. Can anyone help explain why? tks
package main
import (
"io"
"os"
"strings"
"fmt"
)
type testReader struct {
w io.Reader
str string
}
func (tt *testReader) Read (b []byte) (n int, err error) {
io.Copy(os.Stdout, tt.w)
n, err = tt.w.Read(b)
if tt.w !=nil {
return 0,io.EOF
}
return
}
func main() {
s := strings.NewReader("testtesttest!!!")
r := testReader{s,"ttthhh"}
fmt.Println(&r)
io.Copy(os.Stdout, &r)
// s.Seek(0,0) // solution from Poy's answer
io.Copy(os.Stdout, &r)
}
I'm going to prune down the given example to (as there is a bit of noise):
package main
import (
"io"
"os"
"strings"
)
func main() {
s := strings.NewReader("testtesttest")
io.Copy(os.Stdout, s) // Will print "testtesttest"
io.Copy(os.Stdout, s) // Won't print anything
}
The reason the second copy won't output anything is the io.Reader
(s
) has already been read. Reading from a io.Reader
is not idempotent (you can't call it twice to get the same results). It also doesn't have a way to "reset" it or anything.
As @JRLambert pointed out you have s.Seek() and s.Reset() to allow you to start reading again.
Quick addition to all the correct answers (@poy and @JRLambert) provided so far... Use io.TeeReader
or io.MultiWriter
for times when you would want to use io.Copy
more than once. Below are some examples of using each.
Using io.TeeReader
package main
import (
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
)
func main() {
sourceFile, _ := os.Open("source/ebook.pdf")
var buf bytes.Buffer
tee := io.TeeReader(sourceFile, &buf)
process := func(sourceReader io.Reader) {
targetFile, _ := os.Create("target/ebook.pdf")
defer targetFile.Close()
if _, err := io.Copy(targetFile, sourceReader); err != nil {
fmt.Println(err)
}
}
process(tee)
fmt.Println(checksum(&buf))
}
func checksum(buf *bytes.Buffer) string {
h := md5.New()
b, _ := ioutil.ReadAll(buf)
if _, err := h.Write(b); err != nil {
fmt.Println(err)
}
return hex.EncodeToString(h.Sum(nil)[:16])
}
Using io.MultiWriter
package main
import (
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
)
func main() {
sourceFile, _ := os.Open("source/ebook.pdf")
process := func(sourceReader io.Reader) {
targetFile, _ := os.Create("target/ebook.pdf")
defer targetFile.Close()
var buf1, buf2 bytes.Buffer
w := io.MultiWriter(targetFile, &buf1, &buf2)
if _, err := io.Copy(w, sourceReader); err != nil {
fmt.Println(err)
}
fmt.Println(checksum(&buf1))
fmt.Println(checksum(&buf2))
}
process(sourceFile)
}
func checksum(buf *bytes.Buffer) string {
h := md5.New()
b, _ := ioutil.ReadAll(buf)
if _, err := h.Write(b); err != nil {
fmt.Println(err)
}
return hex.EncodeToString(h.Sum(nil)[:16])
}