Golang io.ReadCloser Examples

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.

Advertisement

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:

Advertisement
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"}

Golang io.ReadCloser Examples

 

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

 

Categories GO

Didn't find what you were looking for? Perform a quick search across GoLinuxCloud

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can either use the comments section or contact me form.

Thank You for your support!!

Leave a Comment

X