2018年3月

前面我们讲述了如何基于“go-bindata-assetfs”开源工具包将html/js/css静态文件打包到Go程序中,形成一个一体化Web服务程序(点此查看)。解决方案最核心的一句代码如下:

//静态请求,由AssetFS统一处理。
http.Handle("/", http.FileServer(assetFS()))

我们知道,如果需要对路由做更加灵活的处理,需要使用http.HandleFunc方法。那么上述代码意味着所有访问"/"路径下,未经其他路由处理过的路径都将被“assetfs”处理。这种方式在一些需要动态解析的场景下会存在比较麻烦的问题。

举例说明,在某种场景下我们虽然需要直接显示html文件,但是又必须在html回送给客户端前对其进行解析,加入动态查询的数据(如基于“template”包来进行模版渲染的伪静态文件)。这个时候,我们需要将“assetfs”提供给我们的数据结构进行灵活使用,先获取到html等静态文件的内容,把它当作模板进行解析,然后再输出到客户端。

下面将给出相应的解决方案,可以趁此机会,更好更细节的理解“assetfs”的工作原理。核心代码如下:

fs := assetFS()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  path := r.URL.Path
  if path == "/" || strings.Contains(path, ".html") {
    //根目录默认显示首页。
    if path == "/" {
      path = "/index.html"
    }
    data, _ := fs.Asset("pages" + path)
    html := string(data)
    //这里可以对html页面数据进行处理。
    //...
    w.Header().Add("Content-Type", "text/html; charset=UTF-8")
    w.Write([]byte(html))
  } else {
    //其他处理。
    //...
  }
})

示例中,fs.Asset方法,作用是根据文件路径获取到该文件内容的字节数组。有一点需要注意的是,传入的文件路径必须带有文件编译前的前缀,如某“aaa.html”文件编译时,所在的解决方案路径为“xxx/yyy/”,VSCode终端中运行的是“go-bindata-assetfs xxx/yyy/...”,那么最终在使用fs.Asset方法获取文件内容字节数组时,应使用如下调用格式:

fs.Asset("xxx/yyy/aaa.html")

否则将无法获取到真实的数据。

Go的高性能,使它天生适合开发io方面的服务,Web服务当然不再话下。同时,Go编译后生成的单文件不是字节码,而是对应平台的机器码,因此它效率更高、资源占用更低。

为了更好的进程程序管理,移动程序时更加方便,本文将尝试将Web服务的资源文件打包到Go生成的二进制文件中。

本文将使用一个开源项目“go-bindata-assetfs”来实现目标。该项目的基本原理非常简单,这里描述如下:

1、读取指定的目录,递归的将文件相对路径和字节流的对应关系用变量定义和byte字面值来保存在“bindata.go”文件中。
TIM截图20180227104817.png

2、“bindata.go”文件参与项目编译与构建,从而将文件数据打包进最终生成的二进制文件中。

3、在Web服务需要的地方,构造“http.FileServer”所需要的“FileSystem”参数。此项目自己定义了“AssetFS”结构体,实现了“FileSystem”接口所需要的“Open”方法,在收到request访问指定文件时,由“AssetFS”定义的“Open”方法,经由相关逻辑,直接返回该文件的字节流。
TIM截图20180301101709.png

本例中使用VSCode进行开发与构建,在进行开发前,首先需要先配置VSCode的Go开发环境,并保证所有的Go相关exe可直接在cmd中运行。相关步骤如下:

1、新建工程目录,并创建“main.go”,输入代码。

2、在VSCode终端中执行命令:

go get github.com/jteeuwen/go-bindata/...
go get github.com/elazarl/go-bindata-assetfs/...

TIM截图20180301102053.png

3、创建“static”目录,将相关html/js/css文件都放在该文件夹下。
TIM截图20180301102120.png

4、在终端中执行命令:

go-bindata-assetfs static/...

执行完毕之后,工程目录下会自动生成“bindata.go”文件,该文件下包含“static”文件夹下的所有文件的字节流定义。
TIM截图20180301102157.png

5、定义Web服务相关逻辑,代码如下:

func startWebServer() {
    log.Println("starting web server...")
    //动态请求1。
    http.HandleFunc("/rpc/", ideaLicenseHandler)
    //动态请求2。
    http.HandleFunc("/getUserList", getUserListHandler)
    //静态请求,由AssetFS统一处理。
    http.Handle("/", http.FileServer(assetFS()))
    err := http.ListenAndServe(fmt.Sprintf(":%d", serverPort), nil)
    if err != nil {
        log.Panic("ListenAndServe: ", err)
    }
}

至此,Web服务开发完毕,相关静态资源文件也已打包到二进制文件中。可以打开浏览器查看效果。
TIM截图20180301102637.png