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,编译器会自动将静态文件打包到可执行文件中去,因此如果静态文件太大,也会导致可执行文件也很大。
文章作者:沃航科技