go语言的embed特性在go1.16就已经成熟了,但是官方只提供了实现net.http内部包入的特性。作为go语言下最快的http库,fasthttp由于不兼容net.http,所以导致无法直接使用embed特性的官方例子将静态的文件包入可执行文件并直接使用。小沃最近遇到了这样的需求,记录下如何实现该功能的。
首先是启动embed功能,如
//go:embed www var embededFiles embed.FS
注意:上面的//go:embed www并不是注释,是必须写的,这个是go语言的特殊声明法,是用来声明编译路径下的www文件夹将包入可执行文件,并且通过embededFiles进行访问的,embededFiles支持文件的常规命令如Open,Read,Close等,但是不能Write与Delete。
剩下的只有两部,第一是要自己判断并设置http mime类型,也就是Content-Type头部,第二就是读文件,然后将读到的二进制给对象。
示例代码如下:
package main
import (
"embed"
"io"
"log"
"mime"
"path"
"github.com/valyala/fasthttp"
)
//go:embed www
var embededFiles embed.FS
func fasthttpHttpDialer(ctx *fasthttp.RequestCtx) {
reqpath := string(ctx.Path())
switch reqpath {
case "/api/user/login":
log.Println("登录api")
default: // 作为文件服务使用的部分
/*
下方是访问当前可执行文件同级的真实www目录下的文件的方法,由于已经不需要了,所以注释掉。
handler := fasthttp.FSHandler("www", 0)
handler(ctx)
*/
if reqpath[len(reqpath)-1:] == "/" { // 当请求路径没有添加文件时,默认认为请求的是index.html
reqpath += "index.html"
}
f, err := embededFiles.Open("www" + reqpath) // 这里的www要求与包入可执行文件的资源一致。
if err != nil {
ctx.WriteString("file is not found!!!")
return
}
// 1.利用path.Ext()获取文件的后缀
// 2.利用mime.TypeByExtension()获取对应后缀的html mime类型
// 3.利用ctx.SetContentType()设置http响应的Content-Type
ctx.SetContentType(mime.TypeByExtension(path.Ext(reqpath)))
io.Copy(ctx, f) // 这里使用io.Copy是为了防止文件太大时内存不够用。
f.Close()
}
}
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
log.Println("程序启动了。")
fh := &fasthttp.Server{
Handler: fasthttpHttpDialer,
MaxRequestBodySize: 100 * 1024 * 1024,
}
err := fh.ListenAndServe(":8080")
if err != nil {
log.Println(err)
return
}
}然后正常的go build,编译器会自动将静态文件打包到可执行文件中去,因此如果静态文件太大,也会导致可执行文件也很大。
文章作者:沃航科技