文章目录
  1. 1. filebrowser 源代码结构
  2. 2. 参考

前几天使用filebrowser作为构建流程产出物的文件上传下载服务器,简单翻了下github上的源代码。filebrowser提供了访问文件列表的页面,前端使用vue开发,后端使用golang开发, 最终会生成一个单一的可执行文件,前端页面直接嵌入在二进制可执行文件中。 于是有兴趣研究了下filebrowser中的静态文件嵌入方法。

filebrowser 源代码结构

filebrowser的源代码就是一个正常的golang项目,在go.mod文件中管理依赖,程序的主入口在main.go中。程序的文件夹结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
filebrowser
├── .github
└── workflows
└── build.yml // actions构建流水线
├── .gorelease.yml // gorelease配置文件
├── Makefile // makefile, 构建可执行文件
├── main.go // 主入口
├── cmd
└── root.go // 命令行入口
├── http
├── http.go // http服务,配置URL路由
├── static.go // 处理static文件的hanlder
├── frontend // 前端代码,vue项目
├── assets.go // 静态资源打包
├── assets_dev.go // 开发环境静态资源打包

其中http包配置了所有请求的URL路由,使用gorilla作为http服务框架; 而/static路径的处理器getStaticHandler则定义在文件http/static.go下,主要逻辑是根据请求路径从assetsFs文件系统对象中获取文件内容并返回给客户端。assetsFs则来自于frontend/assets.go中定义的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// http/static.go
func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs fs.FS) (index, static http.Handler) {
...
if !strings.HasSuffix(r.URL.Path, ".js") {
http.FileServer(http.FS(assetsFs)).ServeHTTP(w, r)
return 0, nil
}

fileContents, err := fs.ReadFile(assetsFs, r.URL.Path+".gz")
if err != nil {
return http.StatusNotFound, err
}
...
}

接下来看下frontend/assets.gofrontend/assets_dev.go中的内容。frontend/assets.go文件的开头包含2条指令//go:build !dev, // +build !dev 用来指定在非dev模式下使用;接下来是//go:embed dist/* 则指定将dist目录下的所有文件都嵌入到go文件中, 变量assets为embed.FS类型。frontend/dist目录下的文件则是由frontend下的vue项目编译生成的。

frontend/assets.go

1
2
3
4
5
6
7
8
9
10
11
12
13
//go:build !dev
// +build !dev

package frontend

import "embed"

//go:embed dist/*
var assets embed.FS

func Assets() embed.FS {
return assets
}

frontend/assets_dev.go则指定只在dev模式下生效, assets为fs.FS类型。在dev模式下, 直接使用文件系统路径,而不是嵌入的文件。

frontend/assets_dev.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//go:build dev
// +build dev

package frontend

import (
"io/fs"
"os"
)

var assets fs.FS = os.DirFS("frontend")

func Assets() fs.FS {
return assets
}

参考

文章目录
  1. 1. filebrowser 源代码结构
  2. 2. 参考