Go-Template模板讲解了模板,但是模板本身是静态文件。而在go中,默认情况下,将项目打包成可执行文件时,是不会把依赖的静态资源文件也打包的,所以在部署的时除了要把可执行文件复制到指定目录,还需要把需要的静态文件放到指定位置,这就显得比较繁琐。
而静态文件具有不变性,所以如果能在打包时,把静态资源一并打包,就可以减少部署成本。而go-bindata就可以把静态文件转换为go源文件,就可以实现这个事情。
首先使用下面命令安装
go get -u github.com/jteeuwen/go-bindata/...
安装完毕后,使用下面命令,把我们的a.tpl文件转换为tpl.go文件:
go-bindata -pkg main -o tpl.go a.tpl
使用上面命令我们可以吧a.tpl文件转换为tpl.go文件。其中-o是指定生成的文件的名称为tpl.go,-pkg是指定生成的tpl.go文件的包名为main,a.tpl是目标tpl。
比如a.tpl内容为:
/**创建变量company,保存Company变量**/
{{ $company := .Company}}
/**遍历Company内的所有Person**/
{{ range .Company.Persons -}}
/** 如果名称为"jiaduo" 则先打印person的name,然后打印公司名**/
{{- if eq .Name "JiaDuo" }}
{{ lowerFirst .Name}} {{$.Company.CompanyName}}
{{- else }}
/** 否则先打印公司名称,然后打印Person的name**/
{{$company.CompanyName}} {{ lowerFirst .Name}}
{{- end }}
{{ end -}}
则生成的tpl.go内容为:
// Code generated by go-bindata. (@generated) DO NOT EDIT.
// Package tpl generated by go-bindata.
// sources:
// a.tpl
package main
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
clErr := gz.Close()
if err != nil {
return nil, fmt.Errorf("read %q: %v", name, err)
}
if clErr != nil {
return nil, err
}
return buf.Bytes(), nil
}
type asset struct {
bytes []byte
info os.FileInfo
}
type bindataFileInfo struct {
name string
size int64
mode os.FileMode
modTime time.Time
}
// Name return file name
func (fi bindataFileInfo) Name() string {
return fi.name
}
// Size return file size
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
// Mode return file mode
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
// ModTime return file modify time
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
// IsDir return file whether a directory
func (fi bindataFileInfo) IsDir() bool {
return fi.mode&os.ModeDir != 0
}
// Sys return file is sys mode
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _aTpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xe2\xd2\xd7\xd2\x7a\xda\x31\xfb\xe9\xee\x5d\x4f\xfb\x67\xbc\x6c\xef\x4f\xce\xcf\x2d\x48\xcc\xab\x7c\xbf\xa7\xe7\xc9\xfe\xb9\x4f\xd7\xce\x70\x86\xf0\x21\x92\x5a\x5a\xfa\x5c\xd5\xd5\x0a\x2a\x50\x45\x0a\x56\xb6\x0a\x7a\x50\x05\xb5\xb5\x20\x93\x5e\x36\xf6\x3e\xed\x6b\x83\xe9\x69\x6b\x7d\x3e\xab\xe5\x59\x67\xc3\xb3\x39\x9d\x01\xa9\x45\xc5\xf9\x79\x50\xfd\x45\x89\x79\xe9\xa9\x70\x9d\x7a\x10\xb9\x62\x05\x5d\x88\x19\x0a\x4f\x97\x35\x3d\x9b\x37\xe7\xe9\x84\xde\xe7\xcb\x37\x3c\xd9\xb1\x4b\x29\x2b\x33\x31\xa5\x34\x5f\x49\xe1\x69\xc7\xcc\xa7\xad\x1d\xcf\x3a\x27\x3f\xed\xdd\x50\x00\xd6\xf3\x7c\x56\x4b\x5e\x62\x6e\xea\xfb\x3d\x3d\xcf\x5b\xb6\x3d\x9d\xd0\x07\x91\x7b\xda\xba\xe6\x69\xff\x8e\xa7\x13\x7a\x21\xd6\xe9\x2a\x64\xa6\x29\xa4\x16\x2a\xe8\xf9\x25\xe6\xa6\x2a\x28\x79\x65\x26\xba\x80\x4c\xab\xad\x05\x39\x25\x27\xbf\x3c\xb5\xc8\x2d\xb3\xa8\xb8\x04\x22\x5f\x5b\xab\x50\x5d\xad\x02\x77\x1a\x94\x86\xc8\x80\xcd\x4a\xcd\x29\x4e\x55\x80\x39\x74\xc2\x32\x64\x37\xc1\xed\x7d\xbe\x7c\x03\x9a\x93\x02\x90\x9d\x0b\x71\x16\x2c\x10\x51\xed\x50\xc0\xea\x26\x88\xcd\x79\x29\x50\x47\x83\x58\xa0\xc0\x02\x04\x00\x00\xff\xff\x10\xa9\xc3\x82\xbc\x01\x00\x00")
func aTplBytes() ([]byte, error) {
return bindataRead(
_aTpl,
"a.tpl",
)
}
func aTpl() (*asset, error) {
bytes, err := aTplBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "a.tpl", size: 444, mode: os.FileMode(420), modTime: time.Unix(1653013726, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[canonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
}
return a.bytes, nil
}
return nil, fmt.Errorf("Asset %s not found", name)
}
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}
return a
}
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[canonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
}
return a.info, nil
}
return nil, fmt.Errorf("AssetInfo %s not found", name)
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"a.tpl": aTpl,
}
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("nonexistent") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
canonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(canonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
}
}
if node.Func != nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
rv = append(rv, childName)
}
return rv, nil
}
type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
"a.tpl": &bintree{aTpl, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}
func _filePath(dir, name string) string {
canonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
}
可知tpl.go中把a.tpl转换为了二进制数据。
下面我们看如何使用生成的tpl.go文件:
func useBinData(data *Data) error {
// 获取a.tpl文件对应的二进制数据
bs, err := Asset("a.tpl")
if err != nil {
return err
}
// 创建模板:指定模板名称,模板函数,模板内容
tpl := template.Must(template.New("ap.tpl").Funcs(FuncMap).Parse(string(bs)))
// 执行模板渲染
buf := bytes.NewBuffer([]byte{})
err = tpl.Execute(buf, data)
if err != nil {
return err
}
// 输出渲染结果
fmt.Println(buf.String())
return nil
}
如上其中方法Asset是tpl.go中自动生成的函数,目的是根据tpl文件名返回对应的tpl文件的二进制数据。
戳下面阅读
golang并发教程 ForkJoinPool K8s网络模型
点亮再看哦