1 Star 0 Fork 1

sysadm/资源管理库

forked from kitfast/资源管理库 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
server_webui.go 3.98 KB
一键复制 编辑 原始数据 按行查看 历史
heiing 提交于 2024-06-18 17:40 . WebUI新增模板解析
package resml
import (
"bytes"
"embed"
"fmt"
"html/template"
"io"
"io/fs"
"net/http"
"path/filepath"
"strings"
"sync"
"time"
)
// 嵌入 webui 文件系统。
// 使用方法:
//
// http.Handle("/prefix/", http.StripPrefix("/prefix/", http.FileServerFS(webui)))
//
// 注:go:embed 与它前面的 // 之间不能有空格,否则不会嵌入文件。
//
//go:embed all:webui
var webui embed.FS
// WebUI 组合 fs.FS 与 http.Handler 接口。
// http.FileSystem 接口与 fs.FS 兼容,可以直接转为 http.FileSystem。
type WebUi interface {
fs.FS
http.Handler
}
// Web UI 的变量
type WebUiVars struct {
Title string // 标题
}
// WebUI 文件系统。
type webUiHandler struct {
mu sync.Mutex
prefix string
index *TemplateFile
vars *WebUiVars
}
// 实现 http.FileSystem 接口。
func (fs *webUiHandler) Open(name string) (fs.File, error) {
fs.mu.Lock()
defer fs.mu.Unlock()
// 此处的 /webui 是嵌入文件系统的前缀,是固定值,而不是请求 path 的前缀
name = strings.TrimPrefix(name, "/webui/")
if !strings.HasPrefix(name, "webui/") {
name = fmt.Sprintf("webui/%s", strings.TrimLeft(name, "/"))
}
if strings.HasSuffix(name, "/.") {
name = name[:len(name)-1] + "index.html"
}
fd, err := webui.Open(name)
if err != nil {
return nil, err
}
if stat, err := fd.Stat(); err == nil && stat.IsDir() {
if fs.index != nil {
return fs.index.newReader(), nil
}
name = fmt.Sprintf("%s/index.html", strings.TrimRight(name, "/"))
fd, err = webui.Open(name)
if err != nil {
return nil, err
}
}
if strings.HasSuffix(name, "/index.html") {
if fs.index != nil {
return fs.index.newReader(), nil
}
fs.index, err = NewTemplateFile(fd, name, fs.vars)
if err != nil {
return nil, err
}
return fs.index.newReader(), nil
}
return fd, nil
}
// 实现 http.Handler 接口。
func (fs *webUiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if fs.prefix == "" {
http.ServeFileFS(w, r, fs, r.URL.Path)
return
}
name := strings.Trim(strings.TrimPrefix(r.URL.Path, fs.prefix), "/") // 去掉前缀与 / 字符
if name == "" || name == "." {
name = "index.html"
}
http.ServeFileFS(w, r, fs, name)
}
// 创建 WebUi 实例。
func NewWebUi(prefix string) WebUi {
return NewWebUiVars(prefix, nil)
}
// 创建 WebUi 实例。
func NewWebUiVars(prefix string, vars *WebUiVars) WebUi {
h := &webUiHandler{prefix: strings.TrimSpace(prefix), vars: vars}
if h.vars == nil {
h.vars = &WebUiVars{Title: "文件管理系统"}
} else if strings.TrimSpace(h.vars.Title) == "" {
h.vars.Title = "文件管理系统"
}
return h
}
// 获取 WebUI 的嵌入文件系统。它的文件路径为 webui/*,例如 webui/index.html。
func WebUiEmbedFs() embed.FS {
return webui
}
type TemplateFile struct {
rd *bytes.Reader
ln int64
buf []byte
name string
mt time.Time
}
func (f *TemplateFile) newReader() *TemplateFile {
return &TemplateFile{rd: bytes.NewReader(f.buf), ln: int64(len(f.buf)), name: f.name, mt: f.mt}
}
func (f *TemplateFile) Close() error {
return nil
}
func (f *TemplateFile) Read(p []byte) (n int, err error) {
if f.rd != nil {
return f.rd.Read(p)
}
return 0, io.ErrNoProgress
}
func (f *TemplateFile) Stat() (fs.FileInfo, error) {
return f, nil
}
func (f *TemplateFile) Name() string {
return f.name
}
func (f *TemplateFile) Size() int64 {
return f.ln
}
func (f *TemplateFile) Mode() fs.FileMode {
return 0644
}
func (f *TemplateFile) ModTime() time.Time {
return f.mt
}
func (f *TemplateFile) IsDir() bool {
return false
}
func (f *TemplateFile) Sys() any {
return nil
}
func NewTemplateFile(fd fs.File, name string, vars any) (*TemplateFile, error) {
body, err := io.ReadAll(fd)
fd.Close()
if err != nil {
return nil, err
}
t, err := template.New(name).Parse(string(body))
if err != nil {
return nil, err
}
buf := new(bytes.Buffer)
if err = t.Execute(buf, vars); err != nil {
return nil, err
}
return &TemplateFile{buf: buf.Bytes(), name: filepath.Base(name), mt: time.Now()}, nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/sysadm/resml.git
git@gitee.com:sysadm/resml.git
sysadm
resml
资源管理库
master

搜索帮助