Skip to content

简单的http.FileServer使用

Golang web项目中提供静态文件使用,可以用自带的http.FileServer

Sample Code

package main

import (
    "log"
    "net/http"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", home)
    ...

    fileServer := http.FileServer(http.Dir("./ui/static/"))
    // 所有以/static/开头的url,都会被以下handler处理。
    // 为了匹配路径,会将url中的/static去掉,剩下的部分,会在fileserver中查找。
    mux.Handle("/static/", http.StripPrefix("/static", fileServer))

    log.Println("Starting server on :4000")
    err := http.ListenAndServe(":4000", mux)
    log.Fatal(err)
}

该项目的目录结构如下

.
├── cmd
│   └── web
│       ├── handlers.go
│       └── main.go
├── go.mod
├── pkg
├── README.md
└── ui
    ├── html
    │   ├── base.layout.html
    │   ├── footer.partial.html
    │   └── home.page.html
    └── static
        ├── css
        │   └── main.css
        ├── img
        │   ├── favicon.ico
        │   └── logo.png
        └── js

在html页面模板中引用静态文件的代码为

<link rel='stylesheet' href='/static/css/main.css'>
<link rel='shortcut icon' href='/static/img/favicon.ico' type='image/x-icon'>

对应的页面效果为

使用起来很简单,但是还是有些地方需要注意的

  • FileServer将所有请求路径都通过path.Clean() 函数进行了标准化处理,然后才开始查找文件。这样做有助于避免目录遍历攻击。(目录遍历攻击,通常会通过... 等查找上级目录并获取其他平级目录的文件,没有限制,整个磁盘的文件都会泄漏出去)。
func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
	upath := r.URL.Path
	if !strings.HasPrefix(upath, "/") {
		upath = "/" + upath
		r.URL.Path = upath
	}
	serveFile(w, r, f.root, path.Clean(upath), true)
}
  • 支持range request
$ curl -i -H "Range: bytes=100-199" --output - http://localhost:8080/static/img/logo.png
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Length: 100
Content-Range: bytes 100-199/1075
Content-Type: image/png
Last-Modified: Mon, 07 Mar 2022 08:03:41 GMT
Date: Mon, 07 Mar 2022 08:55:44 GMT

h�j��ZbK�&�"b��dS�"V��M�PQ�S�T��x�PMC1���&�.(غ� ����&�"^"� ZI
  • 支持Last-ModifiedIf-Modified-Since 。如果文件在用户上一次请求后没有被修改,那么就会返回304,而不是文件内容。
  • Content-Type 会使用mime.TypeByExtension() 从文件扩展名自动识别。但可以通过mime.AddExtensionType() 函数进行扩展。
  •  http.ServeFile() 如果想要用这个函数来提供单独文件的下载,那么需要注意这个方法本身是没有使用path.Clean() 做url的标准化处理的。所以你使用filepath.Clean() 来自己处理这类安全问题。
  • 禁止显示目录,有两种方法
    • 方法1,在目录下放置空白的index.html文件,那么就会返回响应码200和空的内容。可以使用find ./ui/static -type d -exec touch {}/index.html \; 命令。
    • 方法2, 创建一个自定义的http.FileSystem ,并在访问目录的时候,返回os.ErrNotExist 。 可参考 https://csdaomin.com/2022/03/07/golang-custom-http-filesystem/
Published ingolang

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *