Introduction to Golang io.ReadCloser
In today's post, we will walk through some examples of working with io.ReadCloser
type in the Go io package.
io package:
Package io provides basic interfaces to I/O primitives. Its primary job is to wrap existing implementations of such primitives, such as those in package os, into shared public interfaces that abstract the functionality, plus some other related primitives.
type ReadCloser
: ReadCloser is the interface that groups the basic Read and Close methods.
type ReadCloser interface {
Reader
Closer
}
From go 1.16, the function NopCloser()
was introduced:
func NopCloser(r Reader) ReadCloser:
NopCloser returns a ReadCloser with a no-op Close method wrapping the provided Reader r. If r implements WriterTo, the returned ReadCloser will implement WriterTo by forwarding calls to r.
If you have a type that implements io.Reader
but not io.Closer
(such as strings.Reader
) and need to pass it to a function that expects an io.ReadCloser
, pass your io.Reader
into ioutil.NopCloser
and get back a type that implements io.ReadCloser
. If you look at the implementation, it’s very simple:
type nopCloser struct {
io.Reader
}
func (nopCloser) Close() error { return nil }
func NopCloser(r io.Reader) io.ReadCloser {
return nopCloser{r}
}
Using NonCloser()
function to return ReadCloser
Here is an example of using NonCloser()
function to return a ReadCloser
 variable:
package main
import (
"bytes"
"fmt"
"io"
"os"
"strings"
)
func main() {
readCloser := io.NopCloser(strings.NewReader("Hello GoLinuxCloud Members!")) // r type is io.ReadCloser
fmt.Printf("Type of readCloser is %T", readCloser)
fmt.Println("")
// example to read data
buf := new(bytes.Buffer)
numOfByte, err := buf.ReadFrom(readCloser)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
readCloser.Close()
content := buf.String()
fmt.Printf("Read: %d bytes, content is: %q\r\n", numOfByte, content)
}
Output:
Type of readCloser is io.nopCloserWriterTo
Read: 27 bytes, content is: "Hello GoLinuxCloud Members!"
Note that: Because NewReader
implements WriterTo so the returned ReadCloser
will implement WriterTo
so the type of that variable is io.nopCloserWriterTo
Implementing io.ReadCloser interface
In the below example, we will create a struct with bytes.Buffer*
. Since the bytes.Buffer*
 already implements Reader,
so if we start with that class, we're halfway there. Our todo is implement Close()
function:
package main
import (
"bytes"
"fmt"
"io"
"os"
)
type ClosingBuffer struct {
*bytes.Buffer
}
func (cb *ClosingBuffer) Close() (err error) {
//we don't actually have to do anything here, since the buffer is just some data in memory
//and the error is initialized to no-error
return
}
func main() {
cb := &ClosingBuffer{bytes.NewBufferString("Hi GoCloudMember!")}
var rc io.ReadCloser
rc = cb
buf := new(bytes.Buffer)
numOfByte, err := buf.ReadFrom(rc)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
content := buf.String()
fmt.Printf("Read: %d bytes, content is: %q\r\n", numOfByte, content)
fmt.Printf("Type of cb is %T", cb)
fmt.Println("")
fmt.Printf("Type of rc is %T", rc)
fmt.Println("")
rc.Close()
}
Output:
Read: 17 bytes, content is: "Hi GoCloudMember!"
Type of cb is *main.ClosingBuffer
Type of rc is *main.ClosingBuffer
Convert HTTP response io.ReadCloser to string
You frequently want to read the response when you send a server an HTTP request. The http.Response
object, which is returned as a result of sending a request by an HTTP client, contains the body of the response in Go and is located in the Response.Body
field of the object. In Go, the HTTP response body has the io.ReadCloser
type. You must use the io.ReadAll()
function to read the contents of this field in order to convert the io.ReadCloser
object to a string.
ReadAll():
ReadAll reads from r until an error or EOF and returns the data it read. A successful call returns err == nil, not err == EOF. Because ReadAll is defined to read from src until EOF, it does not treat an EOF from Read as an error to be reported.
Here is an example of writing an HTTP client and server with Response.Body
has the io.ReadCloser
type:
Sever:Â
package main
import (
"encoding/json"
"log"
"net/http"
)
func main() {
cusHandler := http.HandlerFunc(handleReq)
http.Handle("/goLinuxCloud", cusHandler)
http.ListenAndServe(":8080", nil)
}
func handleReq(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
w.Header().Set("Content-Type", "application/json")
response := make(map[string]string)
response["message"] = "Hello GoLinuxCloud Members!"
response["time"] = "10/26/2022"
// marshall json
json, err := json.Marshal(response)
if err != nil {
log.Fatalf("Error JSON marshal: %s", err)
}
w.Write(json)
return
}
Client:
package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
client := &http.Client{}
request, err := http.NewRequest(http.MethodGet, "http://localhost:8080/goLinuxCloud", nil)
if err != nil {
log.Fatal(err)
}
response, err := client.Do(request)
if err != nil {
log.Fatal(err)
}
body := response.Body
fmt.Printf("Type of readCloser is %T", body)
fmt.Println("")
bytes, err := io.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(bytes))
}
Output:
Type of readCloser is *http.bodyEOFSignal
{"message":"Hello GoLinuxCloud Members!","time":"10/26/2022"}
Summary
To release the allocated resource when dealing with files or any other resource we have to close them (or release memory or our resource). When you have the Read
and Close
methods, you can use io.ReadCloser
type. I have shown you some examples of implementing io.ReadCloser
by using NopCloser()
function or writing your own code.
References
https://pkg.go.dev/io#ReadCloser
https://pkg.go.dev/io
Uses of io.ReadCloser
Golang io/ioutil NopCloser