embed.FS în Go

Include fișiere și directoare direct în executabil la compilare, ca programul să nu mai depindă de fișiere externe.

Sintaxă
import "embed"

//go:embed static/* sabloane
var fisiere embed.FS

//go:embed versiune.txt
var versiune string

Ce face embed.FS

Directiva //go:embed, pusă chiar deasupra unei variabile la nivel de pachet, îi spune compilatorului să copieze fișierele în executabil. Variabila poate fi string sau []byte (pentru un singur fișier) sau embed.FS (pentru mai multe fișiere și directoare). Pentru string și []byte scrii import _ "embed".

embed.FS implementează fs.FS, deci îl folosești cu fs.ReadFile, fs.ReadDir, fs.WalkDir, template.ParseFS și http.FileServerFS. Metodele proprii sunt ReadFile, ReadDir și Open. Conținutul este doar pentru citire.

Căile sunt relative la directorul fișierului .go și nu pot conține ... Fișierele care încep cu . sau _ sunt sărite când incluzi un director, dacă nu folosești prefixul all: (//go:embed all:static). Un model care nu găsește niciun fișier oprește compilarea.

Parametri

Parametru Descriere
modele După //go:embed: nume de fișiere, directoare sau modele cu *, separate prin spații.

Ce returnează

Nu se aplică: variabila primește conținutul la compilare.

Exemple

Exemplul 1: Programul își include propriul cod sursă

Ca exemplul să poată rula oriunde, includem chiar fișierul main.go, singurul care sigur există lângă program.

package main

import (
    "embed"
    "fmt"
    "io/fs"
    "strings"
)

//go:embed main.go
var fisiere embed.FS

func main() {
    intrari, _ := fs.ReadDir(fisiere, ".")
    for _, e := range intrari {
        fmt.Println("fișier inclus:", e.Name())
    }

    sursa, err := fisiere.ReadFile("main.go")
    if err != nil {
        fmt.Println("eroare:", err)
        return
    }
    primul, _, _ := strings.Cut(string(sursa), "\n")
    fmt.Println("primul rând:", primul)

    _, err = fisiere.ReadFile("lipsa.txt")
    fmt.Println(err)
}

Rezultat

fișier inclus: main.go
primul rând: package main
open lipsa.txt: file does not exist

Exemplul 2: Un singur fișier ca string

package main

import (
    _ "embed"
    "fmt"
    "strings"
)

//go:embed main.go
var sursa string

func main() {
    fmt.Println("rânduri în sursă:", strings.Count(sursa, "\n"))
    fmt.Println("conține //go:embed:", strings.Contains(sursa, "//go:embed"))
}

Rezultat

rânduri în sursă: 14
conține //go:embed: true

Exemplul 3: Site static și șabloane într-un singur executabil

Structura proiectului: main.go, static/stil.css, static/logo.png, sabloane/index.html. După go build, executabilul poate fi copiat singur pe server.

package main

import (
    "embed"
    "html/template"
    "io/fs"
    "log"
    "net/http"
)

//go:embed static sabloane/*.html
var continut embed.FS

func main() {
    pagini := template.Must(template.ParseFS(continut, "sabloane/*.html"))
    fs.WalkDir(continut, ".", func(cale string, d fs.DirEntry, err error) error {
        log.Println("inclus:", cale)
        return nil
    })

    http.Handle("GET /static/", http.FileServerFS(continut))
    http.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
        pagini.ExecuteTemplate(w, "index.html", map[string]string{"Oras": "Iași"})
    })
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Rezultat în terminal (serverul rămâne pornit)

2025/03/14 10:00:00 inclus: .
2025/03/14 10:00:00 inclus: sabloane
2025/03/14 10:00:00 inclus: sabloane/index.html
2025/03/14 10:00:00 inclus: static
2025/03/14 10:00:00 inclus: static/logo.png
2025/03/14 10:00:00 inclus: static/stil.css

Greșeli frecvente

  • Pui //go:embed deasupra unei variabile locale (în interiorul unei funcții). Directiva funcționează doar pentru variabile de la nivelul pachetului.
  • Lași un rând gol sau un comentariu între directivă și variabilă, ori scrii // go:embed cu spațiu. Directiva este ignorată sau compilarea eșuează.
  • Te aștepți ca fișierele .env sau _partial.html să fie incluse cu //go:embed sabloane. Fișierele care încep cu . sau _ sunt sărite; folosește all:sabloane.
  • Modifici fișierele de pe disc și te aștepți ca programul să vadă schimbarea. Conținutul este cel de la compilare; recompilează.

Comenzi înrudite

  • os.DirFS: Transformă un director de pe disc într-un fs.FS, sistemul de fișiere abstract din Go.
  • os.CopyFS: Copiază tot conținutul unui sistem de fișiere (fs.FS) într-un director de pe disc.
  • filepath.WalkDir: Parcurge recursiv un director și apelează o funcție pentru fiecare fișier și subdirector, în ordine alfabetică.