• English
  • Example: io.ReadAll

    io.ReadAll reads from an io.Reader until it reaches EOF, returning all bytes that were successfully read as a single []byte.

    It is ideal for scenarios where the data size is bounded and can be safely loaded into memory at once, such as:

    • Reading configuration files
    • Parsing HTTP responses
    • Loading JSON data
    • Handling test data

    Since io.ReadAll buffers the entire input in memory before returning, its peak memory usage is roughly proportional to the size of the input. Reading unbounded or attacker-controlled data may therefore lead to excessive memory consumption or even process termination.

    Quick Start

    Read all data from an in-memory Reader.

    package main
    
    import (
        "fmt"
        "io"
        "strings"
    )
    
    func main() {
        // 1. Create a Reader to simulate a data source
        src := strings.NewReader("Hello, io.ReadAll from gobase.net.\n")
    
        // 2. Read all data from the Reader
        data, err := io.ReadAll(src)
        if err != nil {
            fmt.Printf("failed to read: %v\n", err)
            return
        }
    
        // 3. 'data' now contains all bytes returned by the Reader
        fmt.Printf("%s", data)
    }
    

    Reading Configuration Files

    In production, io.ReadAll is best suited for reading small, bounded resources such as configuration files, certificates, templates, or embedded assets.

    Code example:

    package main
    
    import (
        "fmt"
        "io"
        "os"
    )
    
    func main() {
        // 1. Open the configuration file
        file, err := os.Open("config.json")
        if err != nil {
            fmt.Printf("failed to open file: %v\n", err)
            return
        }
        defer file.Close()
    
        // 2. Read the entire file into memory
        content, err := io.ReadAll(file)
        if err != nil {
            fmt.Printf("failed to read file: %v\n", err)
            return
        }
    
        // 3. Process the configuration data
        fmt.Printf("config file size: %d bytes\n", len(content))
        fmt.Println(string(content))
    }
    

    For small config files like a 20 KB config.json, the cost of loading the entire file into memory is negligible.

    Compared to complex streaming approaches, io.ReadAll provides a simpler implementation and lower maintenance overhead, making it a perfectly reasonable choice for production systems.


    Reading HTTP Response Bodies

    When calling an HTTP API with a predictable and manageable response size, io.ReadAll is one of the most common ways to process the response body.

    Code example:

    package main
    
    import (
        "fmt"
        "io"
        "net/http"
    )
    
    func main() {
        resp, err := http.Get("https://hacker-news.firebaseio.com/v0/item/8863.json?print=pretty")
        if err != nil {
            fmt.Printf("request failed: %v\n", err)
            return
        }
        defer resp.Body.Close()
    
        // Limit the max read size to protect memory against oversized responses
        body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
        if err != nil {
            fmt.Printf("failed to read response body: %v\n", err)
            return
        }
    
        fmt.Printf("response size: %d bytes\n", len(body))
        fmt.Println(string(body))
    }
    

    Here, io.LimitReader caps the maximum read at 1 MB. This prevents an unexpectedly large response from the server from putting heavy memory pressure on the client.