Go 开发中的go:embed应用与最佳实践

go:embed 使用介绍

go:embedGo 语言的一项编译器指令,它允许在编译时将任意文件或目录嵌入到 Go 的二进制文件中。通过这种方式,开发者可以在不依赖外部文件的情况下直接在代码中访问这些资源,从而提高了程序的独立性和资源管理的灵活性。

特点

  • 文件嵌入类型: 对于单个文件,go:embed 可以将其嵌入为字符串 (string) 或字节切片 ([]byte)。对于多个文件或目录,则可以嵌入为文件系统 (embed.FS)。
  • 灵活性: 即使嵌入的文件变量未被显式使用,只要导入了 embed 包,文件仍然会被嵌入。
  • 语法要求: go:embed 指令必须紧跟在要嵌入文件的变量声明之前,并且该变量必须是 string[]byteembed.FS 类型,其他类型(如自定义类型)不支持。

使用方法

go:embed 的用法非常简单,以下是几种常见的应用场景:

import "embed"

//go:embed mobile.txt
var mobile string

//go:embed hello.txt
var contentBytes []byte

//go:embed hello.txt
var fileFS embed.FS

在上述示例中,mobile.txt 文件被嵌入到 mobile 字符串中,hello.txt 文件被嵌入为字节切片,而 fileFS 则作为文件系统,通过 fileFS.ReadFile("hello.txt") 来读取文件内容。

嵌入为字符串

适合嵌入小型的文本文件,如配置文件、模板等。

package main
import (
    _ "embed"
    "fmt"
)

//go:embed hello.txt
var pfinal string

func main() {
    fmt.Println(pfinal)
}

嵌入为字节切片

适合嵌入非文本文件,如图片、字体等二进制数据。

package main
import (
    _ "embed"
    "fmt"
)

//go:embed hello.txt
var pfinal []byte

func main() {
    fmt.Println(pfinal)
}

嵌入为 embed.FS

适合嵌入多个文件或整个目录,嵌入后可以使用 embed.FS 提供的接口来读取文件。

package main
import (
    _ "embed"
    "embed"
    "fmt"
)

//go:embed hello.txt
var pfinal embed.FS

func main() {
    data, _ := pfinal.ReadFile("hello.txt")
    fmt.Println(string(data))
}

多文件嵌入

go:embed 支持在 embed.FS 上嵌入多个文件,多个文件可以通过多行 go:embed 指令来实现(注意 string[]byte 不能有多个 go:embed 指令)。

//go:embed pfinal.txt
//go:embed hello.txt
var pfinal embed.FS

func main() {
    data, _ := pfinal.ReadFile("pfinal.txt")
    fmt.Println(string(data))
    
    data, _ = pfinal.ReadFile("hello.txt")
    fmt.Println(string(data))
}

嵌入目录

通过 go:embed,我们还可以嵌入整个目录,除了以 ._ 开头的文件默认不被嵌入之外,其他文件都会嵌入。

//go:embed pfinal/*
var pfinal embed.FS

func main() {
    data, _ := pfinal.ReadFile("pfinal/.pfinal.txt")
    fmt.Println(string(data))
}

如果需要嵌入以 ._ 开头的文件,必须明确使用通配符,例如 go:embed p/*。注意通配符不具备递归嵌入的功能,如果要嵌入子文件夹,需要在子文件夹中也使用通配符。

只读限制

嵌入的文件内容是只读的,不能在运行时修改这些嵌入的文件。embed.FS 作为只读的虚拟文件系统,支持基本的文件读取操作,但不支持写入。因此,多个 Goroutine 可以并发访问而不会出现竞争条件。

type FS
    func (f FS) Open(name string) (fs.File, error)
    func (f FS) ReadDir(name string) ([]fs.DirEntry, error)
    func (f FS) ReadFile(name string) ([]byte, error)

文件模式匹配

go:embed 指令中可以指定文件夹路径,如果仅指定文件夹路径,除 ._ 开头的文件以外,所有文件都会被嵌入,且嵌入操作是递归的。如果希望嵌入特定文件,或者嵌入以 ._ 开头的文件,必须使用通配符 *

  • 嵌入多个文件:

    //go:embed "hello.txt" "hello-2.txt"
    var f embed.FS
  • 嵌入当前目录的所有文件(除了 ._ 开头的文件):

    //go:embed *
    var f embed.FS

示例:嵌入数据并读取

以下是一个嵌入二进制文件(如数据库文件)的示例:

package main

import (
    "embed"
    "fmt"
)

//go:embed phone.dat
var fsContent embed.FS

func main() {
    data, err := fsContent.ReadFile("phone.dat")
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
}

该示例中,phone.dat 文件被嵌入到 fsContent 中,可以在程序运行时直接读取该文件内容。

标签: Go

相关文章

如何使用Go编写跨平台组件并让Java或PHP调用

在现代软件开发中,跨语言调用是一个常见的需求。假设我们有一个用Go语言编写的组件,我们希望Java或PHP能够直接调用这个组件中对外提供的方法。为了实现这一目标,我们可以使用以下几种方法:1. ...

图片Base64编码

CSR生成

图片无损放大

图片占位符

Excel拆分文件