Distribusi program paling nyaman ketika seluruh kebutuhannya berada dalam satu binary: template, berkas konfigurasi contoh, sampai aset halaman web. Package embed menyematkan berkas statis ke dalam binary pada saat kompilasi, sehingga program berjalan tanpa bergantung pada berkas di disk. Chapter ini menyematkan satu berkas teks dan satu folder penuh, lalu membacanya kembali lewat API sistem berkas standar.

Menyematkan String dengan import _ “embed” #

Bentuk paling sederhana menyematkan isi satu berkas ke variabel string. Directive //go:embed ditulis tepat di atas deklarasi variabel, dan package embed diimpor secara kosong karena yang dipakai adalah efek kompilasinya, bukan identifier-nya:

go
import _ "embed"

//go:embed pesan.txt
var pesan string

Setelah kompilasi, variabel pesan berisi seluruh isi pesan.txt sebagai string biasa — tidak ada pembacaan disk saat program berjalan. Bentuk []byte juga didukung untuk data biner seperti gambar.

embed.FS untuk Banyak Berkas #

Berkas banyak sekaligus dikelola tipe embed.FS — sistem berkas virtual hanya-baca yang aman dipakai dari banyak goroutine. Pola direktori mengambil seluruh isi folder:

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

embed.FS memenuhi interface fs.FS dari package io/fs, sehingga seluruh API standar berlaku: ReadDir mendaftar entri (terurut menurut nama), ReadFile mengambil isi berkas, Open membuka berkas sebagai fs.File, dan fs.WalkDir menelusuri pohon direktori. Fungsi fs.Sub memotong awalan path bila perlu.

Aturan Pola //go:embed #

Pola pada directive mengikuti aturan ketat. Path ditulis relatif terhadap folder paket yang berisi berkas .go; path absolut dan .. tidak diizinkan sehingga tidak ada penyematan dari luar modul. Pola * mengambil semua entri kecuali yang berawalan . atau _; menyebut nama direktori mengambil seluruh isinya secara rekursif.

embed vs os.ReadFile #

Aspekembedos.ReadFile
Sumber datadi dalam binarydisk saat program berjalan
Binary mandiriya — cukup satu berkas programtidak — berkas wajib ikut terdistribusi
Perubahan isiperlu kompilasi ulangcukup ganti berkas
Cocok untuktemplate, aset, konfigurasi bawaandata pengguna, berkas besar

embed mengutamakan kemandirian binary; os.ReadFile mengutamakan isi yang dapat berubah tanpa kompilasi ulang. Chapter Membaca Berkas membahas jalur os secara penuh.

Melayani Berkas dengan http.FS #

Karena embed.FS adalah fs.FS, package net/http dapat langsung menyajikannya lewat http.FS:

go
mux.Handle("GET /statis/", http.StripPrefix("/statis/", http.FileServer(http.FS(statis))))

Pola di atas mendaftarkan rute GET /statis/ pada ServeMux — pola method yang tersedia sejak Go 1.22. Chapter File Server membedah penyajian berkas statis lebih jauh.

Program Utuh #

Berkas embed-demo.go menyematkan dua sumber sekaligus: pesan.txt dan folder statis/. Isi pesan.txt:

text
Halo dari berkas yang ikut terkompilasi ke dalam binary!

Isi statis/tentang.md:

text
# Tentang Aplikasi
Aplikasi contoh untuk chapter embed.
Berkas ini ikut terkompilasi ke dalam binary.

Isi statis/gaya.css:

css
body {
  font-family: sans-serif;
  margin: 2rem auto;
  max-width: 40rem;
}
h1 {
  color: #1a73e8;
}

Program lengkapnya:

go
package main

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

//go:embed pesan.txt
var pesan string

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

// embed.FS memenuhi interface fs.FS sehingga API io/fs standar bisa dipakai.
var _ fs.FS = statis

func main() {
	// String tertanam tersedia sebagai nilai biasa, tanpa membaca disk.
	fmt.Println("pesan  :", pesan)

	// ReadDir mendaftar isi direktori tertanam, terurut menurut nama.
	entri, err := statis.ReadDir("statis")
	if err != nil {
		fmt.Println("gagal membaca direktori:", err)
		return
	}
	for _, e := range entri {
		fmt.Println("entri  :", e.Name())
	}

	// fs.ReadFile membaca satu berkas dari sistem berkas tertanam.
	isi, err := fs.ReadFile(statis, "statis/tentang.md")
	if err != nil {
		fmt.Println("gagal membaca berkas:", err)
		return
	}
	fmt.Printf("tentang: %d byte\n%s\n", len(isi), isi)
}

Jalankan program dari folder chapter:

bash
go run embed-demo.go

Output yang Dihasilkan:

text
pesan  : Halo dari berkas yang ikut terkompilasi ke dalam binary!
entri  : gaya.css
entri  : tentang.md
tentang: 102 byte
# Tentang Aplikasi
Aplikasi contoh untuk chapter embed.
Berkas ini ikut terkompilasi ke dalam binary.

Mari kita telaah baris demi baris program di atas:

  1. Baris 4-6: tiga package diimpor — embed untuk tipe FS, fmt untuk mencetak, dan io/fs untuk API berkas standar.
  2. Baris 9-10: directive //go:embed pesan.txt menyematkan isi berkas ke variabel pesan bertipe string.
  3. Baris 12-13: pola statis/* menyematkan seluruh berkas di folder statis ke variabel statis bertipe embed.FS.
  4. Baris 16: deklarasi var _ fs.FS = statis menegaskan pada saat kompilasi bahwa embed.FS memenuhi interface fs.FS.
  5. Baris 20: string tertanam dicetak seperti nilai biasa — tanpa operasi berkas.
  6. Baris 23-30: ReadDir mendaftar entri folder tertanam; gaya.css dan tentang.md muncul terurut menurut nama.
  7. Baris 33-38: fs.ReadFile membaca statis/tentang.md sampai 102 byte, lalu isi dan panjangnya dicetak.

Referensi #

Perjalanan section Standard Library tuntas: dua belas package — dari fmt yang mencetak, os dan io yang berbicara dengan sistem, sampai embed yang menggabungkan semuanya dalam satu binary. Package lain akan menyusul seiring kebutuhan: encoding/json saat bertukar data, net/http saat melayani permintaan. Seluruhnya berdiri di atas fondasi yang baru Anda kuasai.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

embed-demo.go

Unduh berkas
go
package main

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

//go:embed pesan.txt
var pesan string

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

// embed.FS memenuhi interface fs.FS sehingga API io/fs standar bisa dipakai.
var _ fs.FS = statis

func main() {
	// String tertanam tersedia sebagai nilai biasa, tanpa membaca disk.
	fmt.Println("pesan  :", pesan)

	// ReadDir mendaftar isi direktori tertanam, terurut menurut nama.
	entri, err := statis.ReadDir("statis")
	if err != nil {
		fmt.Println("gagal membaca direktori:", err)
		return
	}
	for _, e := range entri {
		fmt.Println("entri  :", e.Name())
	}

	// fs.ReadFile membaca satu berkas dari sistem berkas tertanam.
	isi, err := fs.ReadFile(statis, "statis/tentang.md")
	if err != nil {
		fmt.Println("gagal membaca berkas:", err)
		return
	}
	fmt.Printf("tentang: %d byte\n%s\n", len(isi), isi)
}

statis/gaya.css

Unduh berkas
css
body {
  font-family: sans-serif;
  margin: 2rem auto;
  max-width: 40rem;
}
h1 {
  color: #1a73e8;
}