//go:embed yönergesi, derleme sırasında dosyaları Go ikili dosyasının içine gömmenizi sağlar. Paket düzeyindeki bir değişkenin hemen üstüne yazılan yönerge, eşleşen dosyanın içeriğini string ya da []byte değişkenine, birden çok dosya ve dizini ise embed.FS tipinde salt okunur bir dosya sistemine yerleştirir. Böylece HTML şablonları, statik web dosyaları, SQL betikleri ya da varsayılan yapılandırma, programla birlikte tek bir dosya olarak dağıtılır ve çalışma anında diskte aranmaz.
Go 1.16 sürüm notlarına göre embed paketi ve yönerge o sürümle birlikte geldi. Bu yazıda üç değişken tipini, desen kurallarını (gizli dosyalar ve all: öneki dahil), embed.FS değerinin io/fs arayüzleriyle birlikte kullanımını, http.FileServerFS ile web'de sunmayı ve derleyicinin verdiği gerçek hata mesajlarını anlatıyoruz. Örnekler bir modül klasöründe go run . ile Go 1.27.3 kullanılarak çalıştırıldı; modül kurulumu için Go modül yönetimi yazısına bakın.
Üç değişken tipi: string, []byte ve embed.FS
Tek bir dosyayı gömüyorsanız string ya da []byte genellikle en iyisidir. Bu iki tip için yönerge yalnızca tek bir desen alabilir ve desen yalnızca tek bir dosyayla eşleşmelidir. Dosya ağacı gerekiyorsa embed.FS kullanırsınız. Belgelere göre string ya da []byte kullanıyor olsanız bile embed paketini içe aktarmanız gerekir; kaynak dosyada embed.FS geçmiyorsa boş içe aktarma (import _ "embed") yeterlidir.
Örnek modülün yapısı şöyle: kök dizinde go.mod, main.go ve surum.txt; altında static/index.html, static/css/site.css ile birlikte iki "gizli" dosya, static/.env ve static/_taslak.txt bulunuyor.
ornek/
go.mod
main.go
surum.txt
static/
.env
_taslak.txt
index.html
css/site.csspackage main
import (
"embed"
"fmt"
"io/fs"
"strings"
)
//go:embed surum.txt
var surum string
//go:embed surum.txt
var surumBayt []byte
//go:embed static
var statik embed.FS
//go:embed all:static
var statikHepsi embed.FS
func listele(ad string, fsys fs.FS) {
fmt.Println(ad + ":")
fs.WalkDir(fsys, ".", func(yol string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
fmt.Println(" " + yol)
}
return nil
})
}
func main() {
fmt.Printf("%q %d bayt\n", strings.TrimSpace(surum), len(surumBayt))
veri, err := statik.ReadFile("static/index.html")
if err != nil {
fmt.Println("hata:", err)
return
}
fmt.Print(string(veri))
_, err = statik.ReadFile("index.html") // kök "static/" değil, paket dizinidir
fmt.Println("hata:", err)
listele("static", statik)
listele("all:static", statikHepsi)
}
// Çıktı:
// "sürüm 1.4.2" 14 bayt
// <h1>Merhaba</h1>
// hata: open index.html: file does not exist
// static:
// static/css/site.css
// static/index.html
// all:static:
// static/.env
// static/_taslak.txt
// static/css/site.css
// static/index.htmlÇıktı birkaç kuralı birlikte gösteriyor. surum değişkeni dosyanın satır sonu dahil tüm içeriğini taşıyor; len(surumBayt) 14 çünkü ü harfleri UTF-8'de ikişer bayt yer kaplıyor (bu konu Go string, rune ve UTF-8 yazısında). embed.FS içindeki yollar her zaman paket dizinine görelidir: static dizinini gömmüş olsanız da dosyanın adı static/index.html olarak kalır ve index.html diye aradığınızda file does not exist hatası alırsınız. Son olarak dizin deseni nokta ve alt çizgiyle başlayan dosyaları dışarıda bıraktı; all: önekiyle ise onlar da gömüldü.
Desen kuralları

Yönerge path.Match söz dizimindeki desenleri kabul eder. Belgedeki kurallar kısaca şunlardır:
- Yönerge, tek bir değişken bildiren satırın hemen üstünde olmalıdır; arada yalnızca boş satır ve
//yorum satırı bulunabilir. - Yönerge yalnızca paket düzeyindeki değişkenlerde kullanılabilir, fonksiyon içindeki yerel değişkenlerde kullanılamaz. Dışa aktarılan ya da aktarılmayan değişkenlerde çalışır.
- Desenler kaynak dosyanın bulunduğu paket dizinine görelidir ve Windows dahil her sistemde ayırıcı
/karakteridir. - Desenler
.,..ya da boş yol öğesi içeremez, eğik çizgiyle başlayıp bitemez. Geçerli dizindeki her şey için.yerine*yazılır. - Bir dizini adlandıran desen alt ağacın tamamını gömer, ama adı
.veya_ile başlayan dosyaları dışarıda bırakır.image/*deseniimage/.tempfiledosyasını gömer,imagegömmez; ikisi deimage/dir/.tempfiledosyasını gömmez. all:önekiyle başlayan desen, nokta ve alt çizgiyle başlayan dosyaları da dahil eder.- Desenler modül dışındaki dosyalarla,
.gitiçeriğiyle, sembolik bağlantılarla,vendor/ile ya dago.modiçeren dizinlerle eşleşemez. Her desen en az bir dosya veya boş olmayan dizinle eşleşmelidir. - Boşluk içeren dosya adları için desen Go'nun çift tırnaklı ya da ters tırnaklı string sabiti olarak yazılabilir. Çok sayıda desen için yönerge alt alta tekrarlanabilir.
Belgedeki örnek bu kuralların birlikte nasıl kullanıldığını iyi gösterir: bir sunucu paketi //go:embed image/* template/* ve hemen altında //go:embed html/index.html satırlarıyla tek bir embed.FS değişkenini doldurur. Belgeye göre bu, //go:embed image template html/index.html yazımına neredeyse eşdeğerdir; tek fark yıldızlı desenin doğrudan o dizindeki nokta ile başlayan dosyaları (örneğin image/.tempfile) da içermesidir. Yani * deseni dizin gezme kuralına değil, doğrudan eşleşmeye dayanır; dizin adının kendisi verildiğinde ise gezme kuralı ve dışlama devreye girer.
embed.FS ve io/fs: aynı arayüz, farklı kaynaklar
embed.FS, io/fs paketinin fs.FS arayüzünü uygular; Open, ReadDir ve ReadFile metotlarına sahiptir ve dizin olmayan dosyalarda io.Seeker ile io.ReaderAt desteği verir. Bu sayede fs.FS bekleyen her fonksiyonla çalışır: fs.WalkDir ağacı sözlük sırasıyla gezer, fs.Glob desene uyan adları bulur, fs.Sub bir alt dizini yeni kök yapar. Aynı kodu testte os.DirFS ile diskten, üretimde gömülü dosyalardan besleyebilirsiniz. Arayüz tabanlı tasarımın mantığı için Go interface yazısına bakın.
io/fs belgesine göre bu arayüzlerdeki yol adları işletim sisteminden bağımsızdır: UTF-8, köksüz ve eğik çizgiyle ayrılmış olmalıdır (x/y/z). Yol ., .. ya da boş öğe içeremez; kök dizin için özel olarak . kullanılır ve /x ya da x/ geçersizdir. embed.FS salt okunur bir değer olduğundan birden fazla goroutine tarafından aynı anda güvenle kullanılabilir.
Bu arayüz birliği geliştirme sırasında da işe yarar. Ön yüz dosyalarını sık değiştirdiğiniz bir aşamada her değişiklikte yeniden derlemek istemezsiniz; kodunuzu fs.FS alacak şekilde yazarsanız geliştirmede os.DirFS("web"), üretimde gömülü dosya sistemini verebilirsiniz. Burada bir güvenlik ayrıntısı vardır: io/fs belgesine göre os.DirFS dizin içindeki başka yerlere işaret eden sembolik bağlantıları denetlemez, dolayısıyla chroot benzeri bir yalıtım mekanizması değildir ve fs.Sub bu durumu değiştirmez; erişimi belirli bir dizin ağacıyla sınırlamak için os.Root önerilir. Gömülü dosya sisteminde bu sorun yoktur, çünkü içerik derleme anında belirlenmiştir ve desenler sembolik bağlantılarla zaten eşleşemez.
Gömülü dosyaları web'de sunmak ve şablonlar
Gömülü bir dizini HTTP üzerinden sunmak için Go 1.22 ile gelen http.FileServerFS fonksiyonunu kullanırsınız; daha eski kodlarda aynı iş http.FileServer(http.FS(icerik)) ile yapılır. Burada sık karşılaşılan sorun yol önekleridir: gömülü dosyanın adı web/static/site.css iken tarayıcı /static/site.css ister. Çözüm, fs.Sub ile web/static alt ağacını kök yapmak ve isteğin önündeki /static/ kısmını http.StripPrefix ile atmaktır. html/template paketinin ParseFS fonksiyonu da şablonları doğrudan aynı dosya sisteminden okur.
package main
import (
"embed"
"fmt"
"html/template"
"io"
"io/fs"
"net/http"
"net/http/httptest"
"os"
)
//go:embed web
var webFS embed.FS
func main() {
// "web/static" alt ağacını kök yapıyoruz: istek yolu /static/site.css
// dosya sisteminde site.css adına karşılık gelsin.
statik, err := fs.Sub(webFS, "web/static")
if err != nil {
panic(err)
}
sablonlar := template.Must(template.ParseFS(webFS, "web/sablon/*.tmpl"))
mux := http.NewServeMux()
mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServerFS(statik)))
mux.HandleFunc("GET /selam/{ad}", func(w http.ResponseWriter, r *http.Request) {
sablonlar.ExecuteTemplate(w, "selam.tmpl", r.PathValue("ad"))
})
ts := httptest.NewServer(mux)
defer ts.Close()
for _, yol := range []string{"/static/site.css", "/selam/Zeynep", "/static/yok.css"} {
resp, err := http.Get(ts.URL + yol)
if err != nil {
panic(err)
}
fmt.Printf("%s %d %s\n", yol, resp.StatusCode, resp.Header.Get("Content-Type"))
io.Copy(os.Stdout, resp.Body)
resp.Body.Close()
}
}
// Çıktı:
// /static/site.css 200 text/css; charset=utf-8
// body{margin:0}
// /selam/Zeynep 200 text/plain; charset=utf-8
// Merhaba Zeynep!
// /static/yok.css 404 text/plain; charset=utf-8
// 404 page not foundProgramın akışını adım adım izleyelim. //go:embed web yönergesi web dizininin altındaki her şeyi (adı nokta ya da alt çizgiyle başlayanlar hariç) webFS değişkenine koydu; dosya adları web/static/site.css ve web/sablon/selam.tmpl oldu. fs.Sub(webFS, "web/static"), Open(ad) çağrısını Open("web/static/" + ad) gibi davranan yeni bir dosya sistemi döndürdü. Mux, /static/ ile başlayan GET isteklerini önce StripPrefix ile site.css adına indirdi, sonra dosya sunucusuna verdi. FileServerFS belgesine göre sunulan dosyaların io.Seeker uygulaması gerekir; embed.FS dizin olmayan dosyalarda bunu sağladığı için ek bir işlem yapmadık. Şablon tarafında ise template.Must, ayrıştırma hatası olursa program başlarken panic üreterek hatalı bir şablonun sessizce yayına çıkmasını engeller.
İçerik türü dosya uzantısından belirlendi (text/css). Var olmayan dosya için sunucu 404 döndürdü. FileServerFS belgesine göre /index.html ile biten istekler sondaki index.html atılarak aynı yola yönlendirilir. Yönlendirme kalıpları ve StripPrefix davranışı için Go net/http web sunucusu yazısına bakabilirsiniz. Gerçek bir sunucuda bu yapı, tüm ön yüz dosyalarını tek bir ikili dosyayla taşımanızı ve kapsayıcı imajını sadeleştirmenizi sağlar; imaj tarafı için Dockerfile yazma rehberi yazısına göz atabilirsiniz.
Derleyicinin verdiği hatalar
//go:embed hataları çalışma anında değil derleme sırasında ortaya çıkar; belgeye göre geçersiz bir desen ya da geçersiz bir eşleşme derlemeyi başarısız kılar. Aşağıdaki üç küçük programı ayrı modül klasörlerinde go run . ile çalıştırdık ve Go 1.27.3'ün verdiği mesajları olduğu gibi aktarıyoruz. İlk programda olmayan bir dosyayı gömmeye çalışıyoruz:
package main
import _ "embed"
//go:embed ayarlar.json
var ayarlar string
func main() {
println(ayarlar)
}$ go run .
main.go:5:12: pattern ayarlar.json: no matching files foundİkinci programda yönergeyi fonksiyon içindeki yerel bir değişkene uyguluyoruz:
package main
import _ "embed"
func main() {
//go:embed go.mod
var icerik string
println(icerik)
}$ go run .
# ornek/hata
./main.go:6:4: go:embed cannot apply to var inside funcÜçüncü programda embed paketini içe aktarmayı unutuyoruz. Mesaj, paketi kullanmıyorsanız boş içe aktarmanın yeterli olduğunu da hatırlatıyor:
package main
//go:embed go.mod
var icerik string
func main() {
println(icerik)
}$ go run .
# ornek/hata
./main.go:3:3: go:embed requires import "embed" (or import _ "embed", if package is not used)Yönerge bir yorum satırıdır; // ile go:embed arasına boşluk koyarsanız (// go:embed) satır yönerge olarak tanınmaz. Bunu import _ "embed" içeren bir programla denediğimizde derleme hatasız geçti ve değişken boş string olarak kaldı; yani hata mesajı bile almazsınız. Diğer derleyici hataları için Go sık karşılaşılan hatalar yazısına bakabilirsiniz.
Ne zaman embed, ne zaman disk?

| Durum | Öneri |
|---|---|
| Programla birlikte değişen şablon, statik dosya, SQL | embed.FS ile gömün |
| Tek bir küçük metin (sürüm, lisans, varsayılan ayar) | string veya []byte |
| Kullanıcının düzenleyeceği yapılandırma | Diskten okuyun (os.ReadFile) |
| Çok büyük, sık değişen medya dosyaları | Diskten ya da ayrı depolamadan sunun |
| Hem testte disk hem üretimde gömülü | Kodu fs.FS alacak şekilde yazın |
Gömülen içerik derleme anında sabitlenir; dosyayı değiştirdiğinizde değişikliğin görünmesi için programı yeniden derlemeniz gerekir. Diskten okuma ve yazma için Go dosya okuma ve yazma, derleme ve dağıtım için Go build ve çapraz derleme yazılarına bakın. Belgeye göre //go:embed desenleri go list çıktısında EmbedPatterns, TestEmbedPatterns ve XTestEmbedPatterns alanlarında görünür; araçlar bu bilgiyi kullanabilir.
Sık yapılan hatalar
- Yolu kök sanmak:
//go:embed staticsonrası dosya adıstatic/index.htmlolur; sunarkenfs.Subkullanın. - Gizli dosyaların gömüldüğünü varsaymak: dizin deseni
.ve_ile başlayanları atlar; gerçekten istiyorsanızall:kullanın. - "//" ile "go:embed" arasına boşluk koymak: yönerge tanınmaz, sessizce boş bir değişken elde edersiniz.
- Üst dizinden dosya istemek:
../içeren desen geçersizdir; gömülecek dosyalar paket dizininde ya da altında olmalıdır. - string değişkene birden çok dosya gömmek: string ve []byte için desen tek dosyayla eşleşmelidir; birden fazla dosya için
embed.FSkullanın. - Windows yol ayırıcısı kullanmak: desenlerde ve
ReadFileçağrılarında her zaman/kullanılır. - Boş bir dizini gömmeye çalışmak: belgeye göre boş dizinlerle eşleşmeler yok sayılır ve her desen en az bir dosya ya da boş olmayan dizinle eşleşmek zorundadır; içinde dosya olmayan bir klasör derleme hatasına yol açar.
- Özel karakterli dosya adları: adında
" * < > ? ` ' | / \ :karakterlerinden biri geçen dosyalar gömülemez; bu tür dosyaları yeniden adlandırın.
Sık Sorulan Sorular
Gömülü dosyaları çalışırken değiştirebilir miyim?
Hayır. embed.FS salt okunur bir dosya sistemidir ve içerik derleme anında belirlenir. Yazılabilir veri gerekiyorsa diskte bir dosya ya da veritabanı kullanın.
embed.FS boş olabilir mi?
Evet. Belgeye göre //go:embed yönergesi olmadan bildirilen bir embed.FS boş bir dosya sistemidir. Testlerde kendi fs.FS uygulamalarınızı doğrulamak için testing/fstest paketi vardır.
Gömülü dosyalar ikili dosyanın boyutunu artırır mı?
Dosyaların içeriği ikili dosyanın içine yerleştirildiği için doğal olarak evet; dosyalar ne kadar büyükse çıktı da o kadar büyür. Bu yüzden büyük medya arşivlerini gömmek yerine ayrı sunmak çoğu zaman daha uygundur.
Şablonları embed ile nasıl kullanırım?
text/template ve html/template paketlerindeki ParseFS fonksiyonu bir fs.FS ve bir ya da daha fazla desen alır. Yukarıdaki örnekteki gibi template.ParseFS(webFS, "web/sablon/*.tmpl") yazarsınız; şablonlar ada göre ExecuteTemplate ile çalıştırılır.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Go öğrenme rehberi · Go net/http web sunucusu · Go dosya okuma ve yazma · Go paket yapısı · Go build ve çapraz derleme · Go modül yönetimi