golang

Examples of Embedding Files in Golang

One of the language’s notable features is the ability to embed resources directly into the executable, eliminating the need for external files and simplifying the resource management. To embed a resource in Go, you can use the embed package which provides a set of utilities and interfaces to work with embedded files. This article explores the concept of embedding in Go with examples.

However, it’s important to note that embedding large files or a large number of files can increase the size of the executable, so it’s essential to strike a balance between simplicity and performance.

Example 1: Embed a String File in Golang

To embed a string file in Go, we can simply use the embed package along with the //go:embed directive. The implementation is illustrated in the following to embed a string file:

package main
import (
    _ "embed"
    "fmt"
)
var (
    //go:embed MyFile.txt
    myFile []byte
)
func main() {
    fmt.Println(string(myFile))
}

We add the “embed” package first in the program. Note that the underscore (_) before “embed” is used to indicate that the “embed” package is imported but is not directly used in the code. It’s a way to silence the “imported but not used” compiler error. After that, we declare the “myFile” variable as a byte slice. It is annotated with a special comment which is “//go:embed MyFile.txt”. This comment instructs the Go compiler to embed the contents of a “MyFile.txt” file into the “myFile” variable. We then have a main() function where the string(myFile) converts the byte slice to a string before printing it.

Thus, the output displays the “MyFile.txt” file content as follows:

Example 2: Embed a File as a Byte Slice in Golang

Similarly, the file in Go can be embedded as the byte slice using the same embed package and comment line. Note that embedding a file as a byte slice loads the entire file contents into the memory when the program is executed.

package main
import (
    _ "embed"
    "fmt"
)
//go:embed NewFile.txt
var f []byte
func main() {
    fmt.Println(f)
}

As required, we import the blank import of the embed package into the program. Next, we create the “f” variable as a byte slice and annotate it with the “//go:embed NewFile.txt” comment. This comment line indicates that the contents of the “NewFile.txt” file should be embedded into the “f” variable. Then, we have the main() function where the “f” variable is called inside the print function to print the byte slice of the specified file.

Hence, the program reads the content of the file as a byte slice which can be seen in the following console screen:

Example 3: Embed Multiple Files in Golang

Moreover, multiple files or directories at the same time can also be embedded in Go using the embed package with the “//go:embed” directive. The following program defines the method to embed multiple files:

package main
import (
    "embed"
    "fmt"
)
//go:embed Folder/*
var f embed.FS
func main() {
    data1, _ := f.ReadFile("Folder/data1.txt")
    fmt.Println(string(data1))
    data2, _ := f.ReadFile("Folder/data2.txt")
    fmt.Println(string(data2))
}

After importing the embed package into our program, we define the “f” variable of the “embed.FS” type to hold the embedded file system. The “//go:embed” directive is used to indicate which files or directories should be embedded. In this case, the Folder/* pattern is used to include all files within the folder directory. We then set up the main() function where the “f.ReadFile” is employed which is provided by the “embed.FS” type to read the contents of “data1.txt” and store it in the “data1” variable.

The data in the various files of the exact same folder are fetched and shown in the following output:

Example 4: Embed a Struct in Golang

Additionally, we can indeed embed one struct within another in Go. Struct embedding is a way to achieve the composition and code reuse by including one struct type as a field in another struct type. Here, we try to embed the struct in another struct.

package main
import "fmt"
type Student struct {
    Name string
    Age  int
}
type Courses struct {
    CName  string
    Student // Embedded field
}
func main() {
    course1 := Courses{
        CName: "Golang",
        Student: Student{
            Name: "Andy",
            Age:  23,
        },
    }
    fmt.Println(course1)
    fmt.Println(course1.Age)
    fmt.Println(course1.Student.Age)
}

Initially, the “Student” struct type is created with two fields of different types. After that, we define another struct which is “Courses”, with two fields: “CName” of type string and “Student” which is an embedded field as commented with the embedded field. This means that the “Courses” struct includes all the fields and methods of the “Student” struct.

We then move to the main() function where an instance of “Courses” named “course1” is created. It is initialized with the values for “CName” and the embedded “Student” struct. The “Student” struct fields are also initialized with values. Finally, we print all the values on the console using the “fmt.Println” statement.

The following output retrieves the values both from the “Courses” struct and the embedded “Student” struct, respectively:

Example 5: Embed a Static File in Golang

The “embed” package provides the ability to embed the individual files, whole directories, or even multiple files/directories at once. So, we can embed the static files directly in the Go binary using this package.

package main
import (
    "embed"
    "io/fs"
    "net/http"
)
//go:embed staticFile
var myData embed.FS
func handler() http.Handler {
    file_system := fs.FS(myData)
    html, _ := fs.Sub(file_system, "staticFile")
    return http.FileServer(http.FS(html))
}
func main() {
    mux := http.NewServeMux()
    mux.Handle("/", handler())
    http.ListenAndServe(":8000", mux)
}

We import three packages here which include the “embed” for embedding the static files, the “io/fs” which provides file system-related utility functions, and the “net/http” package which provides the HTTP server and client implementations. We then use the “//go:embed” directive which embeds the contents of the “staticFile” directory into the binary at compile time.

After that, we declare the “myData” variable using the “embed.FS” type. Here, we use the handler function to create an HTTP handler that serves the static files from the embedded “staticFile” directory. Inside the handler function, the “myData” variable is converted to an “fs.FS” type which is a file system interface. The “fs.Sub” function is also used to obtain a “staticFile” subdirectory from the file system. The http.FileServer() function is then called with the subdirectory as the argument to create an HTTP handler that serves the files from that directory.

Next is the main() function where we set up an HTTP server using “http.NewServeMux” which registers the handler function to handle the requests at the root path (“/”). Here, the server listens on port 8000 using “http.ListenAndServe”.

The output embeds the contents of the “staticFile” directory into the binary and serves the files which are accessed via HTTP:

Conclusion

Embedding the resources in Go provides a convenient way to manage the files and assets within your application. The examples in the article demonstrate how to embed the files, directories, and binary data, showcasing the flexibility of using the “embed” package and the convenience of this feature.

About the author

Kalsoom Bibi

Hello, I am a freelance writer and usually write for Linux and other technology related content