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框架实现转发功能:上传文件并转