Source File
embed.go
Belonging Package
embed
// Copyright 2020 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// Package embed provides access to files embedded in the running Go program.//// Go source files that import "embed" can use the //go:embed directive// to initialize a variable of type string, []byte, or [FS] with the contents of// files read from the package directory or subdirectories at compile time.//// For example, here are three ways to embed a file named hello.txt// and then print its contents at run time.//// Embedding one file into a string://// import _ "embed"//// //go:embed hello.txt// var s string// print(s)//// Embedding one file into a slice of bytes://// import _ "embed"//// //go:embed hello.txt// var b []byte// print(string(b))//// Embedded one or more files into a file system://// import "embed"//// //go:embed hello.txt// var f embed.FS// data, _ := f.ReadFile("hello.txt")// print(string(data))//// # Directives//// A //go:embed directive above a variable declaration specifies which files to embed,// using one or more path.Match patterns.//// The directive must immediately precede a line containing the declaration of a single variable.// Only blank lines and ‘//’ line comments are permitted between the directive and the declaration.//// The type of the variable must be a string type, or a slice of a byte type,// or [FS] (or an alias of [FS]).//// For example://// package server//// import "embed"//// // content holds our static web server content.// //go:embed image/* template/*// //go:embed html/index.html// var content embed.FS//// The Go build system will recognize the directives and arrange for the declared variable// (in the example above, content) to be populated with the matching files from the file system.//// The //go:embed directive accepts multiple space-separated patterns for// brevity, but it can also be repeated, to avoid very long lines when there are// many patterns. The patterns are interpreted relative to the package directory// containing the source file. The path separator is a forward slash, even on// Windows systems. Patterns may not contain ‘.’ or ‘..’ or empty path elements,// nor may they begin or end with a slash. To match everything in the current// directory, use ‘*’ instead of ‘.’. To allow for naming files with spaces in// their names, patterns can be written as Go double-quoted or back-quoted// string literals.//// If a pattern names a directory, all files in the subtree rooted at that directory are// embedded (recursively), except that files with names beginning with ‘.’ or ‘_’// are excluded. So the variable in the above example is almost equivalent to://// // content is our static web server content.// //go:embed image template html/index.html// var content embed.FS//// The difference is that ‘image/*’ embeds ‘image/.tempfile’ while ‘image’ does not.// Neither embeds ‘image/dir/.tempfile’.//// If a pattern begins with the prefix ‘all:’, then the rule for walking directories is changed// to include those files beginning with ‘.’ or ‘_’. For example, ‘all:image’ embeds// both ‘image/.tempfile’ and ‘image/dir/.tempfile’.//// The //go:embed directive can be used with both exported and unexported variables,// depending on whether the package wants to make the data available to other packages.// It can only be used with variables at package scope, not with local variables.//// Patterns must not match files outside the package's module, such as ‘.git/*’ or symbolic links.// Patterns must not match files whose names include the special punctuation characters " * < > ? ` ' | / \ and :.// Matches for empty directories are ignored. After that, each pattern in a //go:embed line// must match at least one file or non-empty directory.//// If any patterns are invalid or have invalid matches, the build will fail.//// # Strings and Bytes//// The //go:embed line for a variable of type string or []byte can have only a single pattern,// and that pattern can match only a single file. The string or []byte is initialized with// the contents of that file.//// The //go:embed directive requires importing "embed", even when using a string or []byte.// In source files that don't refer to [embed.FS], use a blank import (import _ "embed").//// # File Systems//// For embedding a single file, a variable of type string or []byte is often best.// The [FS] type enables embedding a tree of files, such as a directory of static// web server content, as in the example above.//// FS implements the [io/fs] package's [FS] interface, so it can be used with any package that// understands file systems, including [net/http], [text/template], and [html/template].//// For example, given the content variable in the example above, we can write://// http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(content))))//// template.ParseFS(content, "*.tmpl")//// # Tools//// To support tools that analyze Go packages, the patterns found in //go:embed lines// are available in “go list” output. See the EmbedPatterns, TestEmbedPatterns,// and XTestEmbedPatterns fields in the “go help list” output.package embedimport ()// An FS is a read-only collection of files, usually initialized with a //go:embed directive.// When declared without a //go:embed directive, an FS is an empty file system.//// An FS is a read-only value, so it is safe to use from multiple goroutines// simultaneously and also safe to assign values of type FS to each other.//// FS implements fs.FS, so it can be used with any package that understands// file system interfaces, including net/http, text/template, and html/template.//// See the package documentation for more details about initializing an FS.type FS struct {// The compiler knows the layout of this struct.// See cmd/compile/internal/staticdata's WriteEmbed.//// The files list is sorted by name but not by simple string comparison.// Instead, each file's name takes the form "dir/elem" or "dir/elem/".// The optional trailing slash indicates that the file is itself a directory.// The files list is sorted first by dir (if dir is missing, it is taken to be ".")// and then by base, so this list of files://// p// q/// q/r// q/s/// q/s/t// q/s/u// q/v// w//// is actually sorted as://// p # dir=. elem=p// q/ # dir=. elem=q// w/ # dir=. elem=w// q/r # dir=q elem=r// q/s/ # dir=q elem=s// q/v # dir=q elem=v// q/s/t # dir=q/s elem=t// q/s/u # dir=q/s elem=u//// This order brings directory contents together in contiguous sections// of the list, allowing a directory read to use binary search to find// the relevant sequence of entries.files *[]file}// split splits the name into dir and elem as described in the// comment in the FS struct above. isDir reports whether the// final trailing slash was present, indicating that name is a directory.func ( string) (, string, bool) {, = stringslite.CutSuffix(, "/"):= bytealg.LastIndexByteString(, '/')if < 0 {return ".", ,}return [:], [+1:],}var (_ fs.ReadDirFS = FS{}_ fs.ReadFileFS = FS{})// A file is a single file in the FS.// It implements fs.FileInfo and fs.DirEntry.type file struct {// The compiler knows the layout of this struct.// See cmd/compile/internal/staticdata's WriteEmbed.name stringdata stringhash [16]byte // truncated SHA256 hash}var (_ fs.FileInfo = (*file)(nil)_ fs.DirEntry = (*file)(nil))func ( *file) () string { , , := split(.name); return }func ( *file) () int64 { return int64(len(.data)) }func ( *file) () time.Time { return time.Time{} }func ( *file) () bool { , , := split(.name); return }func ( *file) () any { return nil }func ( *file) () fs.FileMode { return .Mode().Type() }func ( *file) () (fs.FileInfo, error) { return , nil }func ( *file) () fs.FileMode {if .IsDir() {return fs.ModeDir | 0555}return 0444}func ( *file) () string {return fs.FormatFileInfo()}// dotFile is a file for the root directory,// which is omitted from the files list in a FS.var dotFile = &file{name: "./"}// lookup returns the named file, or nil if it is not present.func ( FS) ( string) *file {if !fs.ValidPath() {// The compiler should never emit a file with an invalid name,// so this check is not strictly necessary (if name is invalid,// we shouldn't find a match below), but it's a good backstop anyway.return nil}if == "." {return dotFile}if .files == nil {return nil}// Binary search to find where name would be in the list,// and then check if name is at that position., , := split():= *.files:= sortSearch(len(), func( int) bool {, , := split([].name)return > || == && >=})if < len() && stringslite.TrimSuffix([].name, "/") == {return &[]}return nil}// readDir returns the list of files corresponding to the directory dir.func ( FS) ( string) []file {if .files == nil {return nil}// Binary search to find where dir starts and ends in the list// and then return that slice of the list.:= *.files:= sortSearch(len(), func( int) bool {, , := split([].name)return >=}):= sortSearch(len(), func( int) bool {, , := split([].name)return >})return [:]}// Open opens the named file for reading and returns it as an [fs.File].//// The returned file implements [io.Seeker] and [io.ReaderAt] when the file is not a directory.func ( FS) ( string) (fs.File, error) {:= .lookup()if == nil {return nil, &fs.PathError{Op: "open", Path: , Err: fs.ErrNotExist}}if .IsDir() {return &openDir{, .readDir(), 0}, nil}return &openFile{, 0}, nil}// ReadDir reads and returns the entire named directory.func ( FS) ( string) ([]fs.DirEntry, error) {, := .Open()if != nil {return nil,}, := .(*openDir)if ! {return nil, &fs.PathError{Op: "read", Path: , Err: errors.New("not a directory")}}:= make([]fs.DirEntry, len(.files))for := range {[] = &.files[]}return , nil}// ReadFile reads and returns the content of the named file.func ( FS) ( string) ([]byte, error) {, := .Open()if != nil {return nil,}, := .(*openFile)if ! {return nil, &fs.PathError{Op: "read", Path: , Err: errors.New("is a directory")}}return []byte(.f.data), nil}// An openFile is a regular file open for reading.type openFile struct {f *file // the file itselfoffset int64 // current read offset}var (_ io.Seeker = (*openFile)(nil)_ io.ReaderAt = (*openFile)(nil))func ( *openFile) () error { return nil }func ( *openFile) () (fs.FileInfo, error) { return .f, nil }func ( *openFile) ( []byte) (int, error) {if .offset >= int64(len(.f.data)) {return 0, io.EOF}if .offset < 0 {return 0, &fs.PathError{Op: "read", Path: .f.name, Err: fs.ErrInvalid}}:= copy(, .f.data[.offset:]).offset += int64()return , nil}func ( *openFile) ( int64, int) (int64, error) {switch {case 0:// offset += 0case 1:+= .offsetcase 2:+= int64(len(.f.data))}if < 0 || > int64(len(.f.data)) {return 0, &fs.PathError{Op: "seek", Path: .f.name, Err: fs.ErrInvalid}}.offset =return , nil}func ( *openFile) ( []byte, int64) (int, error) {if < 0 || > int64(len(.f.data)) {return 0, &fs.PathError{Op: "read", Path: .f.name, Err: fs.ErrInvalid}}:= copy(, .f.data[:])if < len() {return , io.EOF}return , nil}// An openDir is a directory open for reading.type openDir struct {f *file // the directory file itselffiles []file // the directory contentsoffset int // the read offset, an index into the files slice}func ( *openDir) () error { return nil }func ( *openDir) () (fs.FileInfo, error) { return .f, nil }func ( *openDir) ([]byte) (int, error) {return 0, &fs.PathError{Op: "read", Path: .f.name, Err: errors.New("is a directory")}}func ( *openDir) ( int) ([]fs.DirEntry, error) {:= len(.files) - .offsetif == 0 {if <= 0 {return nil, nil}return nil, io.EOF}if > 0 && > {=}:= make([]fs.DirEntry, )for := range {[] = &.files[.offset+]}.offset +=return , nil}// sortSearch is like sort.Search, avoiding an import.func ( int, func(int) bool) int {// Define f(-1) == false and f(n) == true.// Invariant: f(i-1) == false, f(j) == true., := 0,for < {:= int(uint(+) >> 1) // avoid overflow when computing h// i ≤ h < jif !() {= + 1 // preserves f(i-1) == false} else {= // preserves f(j) == true}}// i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.return}
![]() |
The pages are generated with Golds v0.7.6. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |