Golang HTTP Fileserver

概要

fileserver是静态文件服务,其主要功能是根据提供文件查询与文件传输的功能。由golang标准库提供。

入口函数

func FileServer(root FileSystem) Handler {
	return &fileHandler{root}
}

type fileHandler struct {
	root FileSystem
}

它的入参是一个带有根路径的文件系统,返回值是一个http_handler(处理函数)

FileSystem文件系统,支持访问文件路径,无论使用何种操作系统,文件路径通过‘/’进行分隔

type FileSystem interface {
	Open(name string) (File, error) // 打开一个文件
}

type File interface {
	io.Closer // 关闭文件
	io.Reader // 读文件
	io.Seeker // 文件位置信息
	Readdir(count int) ([]os.FileInfo, error) // 读文件夹
	Stat() (os.FileInfo, error) // 文件状态
}

处理函数

func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
	upath := r.URL.Path
	if !strings.HasPrefix(upath, "/") { // 改写path
		upath = "/" + upath
		r.URL.Path = upath
	}
	serveFile(w, r, f.root, path.Clean(upath), true) // 核心逻辑
}

func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) {
        f, err := fs.Open(name) // 打开文件,全路径是由root+name
        d, err := f.Stat()
	if d.IsDir() { // 如果path指向的是文件夹
		if checkIfModifiedSince(r, d.ModTime()) == condFalse { // 判断文件夹是否更新
			writeNotModified(w)
			return
		}
		setLastModified(w, d.ModTime()) // 写入文件夹更新时间
		dirList(w, r, f) // 见后
		return
	}

	// 如果path指向的是文件
	sizeFunc := func() (int64, error) { return d.Size(), nil }
	serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f) // 这部分代码比较复杂,起主要逻辑是通过request中的range信息,多线程读取文件内容,返回写入response中。从中我们可以看到静态服务器是支持断点续传的http服务
}

文件夹列表

func dirList(w ResponseWriter, r *Request, f File) {
	dirs, err := f.Readdir(-1) // 读文件夹
	sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() }) // 按照文件名进行排序
        // 写html格式的response
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	fmt.Fprintf(w, "<pre>\n")
	for _, d := range dirs {
		name := d.Name()
		if d.IsDir() { // 如果是子目录
			name += "/"
		}
		url := url.URL{Path: name}
		fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name))
	}
	fmt.Fprintf(w, "</pre>\n")
}

用于将字符串路径转换为文件系统

主要的功能是兼容不同的os

定义

type Dir string
// name是当前os的文件路径格式
func (d Dir) Open(name string) (File, error) {
	dir := string(d) // 根目录
	if dir == "" {
		dir = "."
	}
	fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))) // 拼出全路径
	f, err := os.Open(fullName)
	if err != nil {
		return nil, mapDirOpenError(err, fullName)
	}
	return f, nil
}

辅助中间件

// 该函数对象改写request的path,是一个中间件
func StripPrefix(prefix string, h Handler) Handler {
	if prefix == "" {
		return h
	}
	return HandlerFunc(func(w ResponseWriter, r *Request) { // 其定义见后
		if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) { // 前缀匹配
			r2 := new(Request) // 创建一个新的request
			*r2 = *r
			r2.URL = new(url.URL)
			*r2.URL = *r.URL
			r2.URL.Path = p // 改写path
			h.ServeHTTP(w, r2) // 调用hander的处理逻辑
		} else {
			NotFound(w, r)
		}
	})
}

type HandlerFunc func(ResponseWriter, *Request) // 定义一个函数对象

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { // 该函数对象的ServeHTTP方法是执行自身
	f(w, r)
}

put it together

http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值