Golang实践录:静态资源文件整合:web服务

Posted 李迟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang实践录:静态资源文件整合:web服务相关的知识,希望对你有一定的参考价值。

趁着五一放假,趁着有时间,把欠的一些技术集中研究研究,写写文章,好给自己一个交待。
本文研究静态资源文件的在 web 服务器的整合。

基础

Golang 中的 web 服务框架有很多种,本文选取 gin 实现。gin 实现一个 web 服务仅需几行代码,十分方便。但为了适应更复杂的项目,还需要进行一些改进。
web 服务页面的文件,除了 html 外,还有 css、js、图片等文件,为方便管理,将后者放到 static 目录——与前面文章目录保持一致,将前者放到 templates 目录,使用 gin 的 html 模板进行渲染,而且 gin 也支持自定义模板文件,恰好能适合我们的场景。

实践

资源文件

本文使用到的资源文件如下:

$ tree static/
static/
|-- css
|   |-- bootstrap.min.css
|   |-- font-awesome.min.css
|   `-- main.css
|-- favicon.ico
`-- js
    |-- bootstrap.min.js
    |-- jquery-1.8.3.min.js
    |-- jquery-2.0.0.min.js
    `-- main.js

2 directories, 8 files

$ tree templates/
templates/
|-- about.html
|-- about.js
|-- index.html
|-- login.html
|-- login.js
`-- nav.js

0 directories, 6 files

通用方式

为方便对比,先给出通用的 gin 框架,主要代码如下:


// 直接引用文件形式
func webServerFile() {
    fmt.Println("gin test...")
	router := gin.Default()
    
    // 似乎这样做,templates下只能有文件,不能有目录
    router.LoadHTMLGlob("templates/*")
    
    // 将真实目录做不同前缀,方便引用css js等文件
    // 有些自实现的用html文件或js文件,用html前缀。
    router.StaticFS("/js", http.Dir("static/js"))
    router.StaticFS("/css", http.Dir("static/css"))
    router.StaticFS("/html", http.Dir("templates"))

    router.GET("/", HandleIndex)
    router.GET("/index", HandleIndex)
    router.GET("/about", HandleAbout)
    router.GET("/login", HandleLogin)
    
	router.Run(":8081")
}

其中 LoadHTMLGlob 用于加载 html 模板文件,StaticFS 指定静态资源,有两个参数,第一个指定前缀名称(即在 html 或 js 文件中引用时使用的路径,第二个指定真实路径(相对于 web 程序所在目录)。GET 函数用于响应对应的页面,由于响应函数非本文重点,故简单列举如下:

func HandleIndex(ctx *gin.Context) {
    file := path.Join(gPrefix, "index.html")
    ctx.HTML(http.StatusOK, file, gin.H{
		"title":  "Main website",
	})
}

注:gPrefix 将在下文提及。

整合方式

使用 bindata 方式,与上面示例没有本质区别,只是需要手动设置模板加载规则,指定静态资源文件方式也不同。主要代码如下:

// 整合文件形式
func webServerBindata() {
    fmt.Println("gin test.....")
	router := gin.Default()
	t, err := loadTemplate()
	if err != nil {
		panic(err)
	}
	router.SetHTMLTemplate(t)

    // 下面指定的是静态资源文件,与响应get/post的地址无关系
    fsjs := assetfs.AssetFS{
        Asset:     bindata.Asset,
        AssetDir:  bindata.AssetDir,
        AssetInfo: bindata.AssetInfo,
        Prefix:   "static/js",
        Fallback: "index.html",
    }
    router.StaticFS("/js", &fsjs)
    
    fscss := assetfs.AssetFS{
        Asset: bindata.Asset, AssetDir: bindata.AssetDir, AssetInfo: bindata.AssetInfo, 
        Prefix:   "static/css",
        Fallback: "index.html",
    }
    router.StaticFS("/css", &fscss)
    
    fshtml := assetfs.AssetFS{
        Asset: bindata.Asset, AssetDir: bindata.AssetDir, AssetInfo: bindata.AssetInfo, 
        Prefix:   "templates",
        Fallback: "index.html",
    }
    router.StaticFS("/html", &fshtml)

    
    router.StaticFS("/favicon.ico", &fshtml)
    
    // 人为添加前缀,因为前面loadTemplate加载的html路径包含有路径前缀,因此加上
    // 如果手动删除,则此处不需要前缀
    //gPrefix = "templates"

    router.GET("/", HandleIndex)
    router.GET("/index", HandleIndex)
    router.GET("/about", HandleAbout)
    router.GET("/login", HandleLogin)

	router.Run(":8081")
}

自定义模板加载函数如下:

func loadTemplate() (*template.Template, error) {
    fmt.Println("load my template")
	t := template.New("")
    filenames := bindata.AssetNames()
    for _, name := range  filenames {
        if !strings.HasSuffix(name, ".tmpl") && !strings.HasSuffix(name, ".html") && 
           !strings.HasSuffix(name, ".css") && !strings.HasSuffix(name, ".js") && 
           !strings.HasSuffix(name, ".ico") {
			continue
		}
        //fmt.Println("got html file: ", name)
        content, err := bindata.Asset(name)
        if err != nil {
            return nil, err
        }
        t, err = t.New(name).Parse(string(content))
		if err != nil {
			return nil, err
		}
	}
	return t, nil
}

由于在生成 bindata.go 时,指定了2处目录,而-prefix选项只能指定一个前缀,于是干脆不加该参数,因此,生成的代码中,前缀也会出现对应的目录,正因为这样,代码才使用了gPrefix = "templates"手动指定前缀。后来手动删除生成代码的前缀,命令如下:

sed -i 's/templates\\///g' bindata/bindata.go
sed -i 's/static\\///g' bindata/bindata.go

两者关键代码对比如下图所示(注:左侧为未删除前缀的代码)。
在这里插入图片描述

笔者阅读 go-bindata-assetfs 代码尝试添加参数达到目的,未果。

参考

https://jaycechant.info/2020/go-bindata-golang-static-resources-embedding/
http://blog.hotsun168.com/index.php/archives/18/
1.16 版本新方法:
https://www.flysnow.org/2021/02/28/golang-embed-for-web.html

2021.5.5 凌晨

以上是关于Golang实践录:静态资源文件整合:web服务的主要内容,如果未能解决你的问题,请参考以下文章

Golang实践录:使用gin框架实现文件上传转发功能

Golang实践录:使用gin框架实现转发功能:上传文件并转

Golang实践录:使用gin框架实现转发功能:上传文件并转

Golang实践录:ssh及scp的实现

Golang实践录:ssh及scp的实现

Golang实践录:ssh及scp的实现