MENU

Gin + go:embed 实现静态资源嵌入

May 18, 2024 • Read: 690 • 编码,Go

Go 语言单文件 Web 服务实现

使用 Go 语言开发一些简单的 Web 项目时,往往需要使用一些前端静态资源文件。那有没有什么办法可以把前端资源嵌入到 Go 程序中,最好可以和 Go 程序的接口共用一个 HTTP 服务,这样在运行是也不需要考虑跨域的问题,而且单文件服务部署相比后端程序 + 静态文件的部署方式更简单。

如何嵌入资源文件

Go 在 1.16 版本增加了一个特性 go:embed,可以在编译时嵌入文件或者目录,我们可以使用这个命令实现资源文件的嵌入。

示例项目目录如下:

ginweb
 ├── go.mod
 ├── go.sum
 ├── main.go            // 主程序
 └── web                // 需要嵌入的资源文件
     ├── index.html    // 静态资源文件
     └── photo.jpg    // 静态资源文件

例如通过 go:embed 将项目中的 web 目录下的所有文件进行嵌入,这时 WebDir 变量就是一个虚拟的文件系统,可以直接当作文件进行操作。

var (
    //go:embed web/*
    WebDir embed.FS
)

使用 HTTP 服务加载资源

// 通过 Gin 的 StaticFS() 方法,可以将嵌入的资源起一个 HTTP 服务,现在访问 2333 端口默认就会加载 /static/index.html 文件。

package main

import (
    "embed"
    "github.com/gin-gonic/gin"
    "io/fs"
    "net/http"
)

var (
    //go:embed web/*
    WebDir embed.FS
    App    *gin.Engine
)

func main() {
    App = gin.New()
    staticFp, _ := fs.Sub(WebDir, "web")
    // 起一个静态资源 HTTP 服务
    App.StaticFS("/static", http.FS(staticFp))
    // 这是一个接口
    App.GET("/api", func(context *gin.Context) {
        context.JSONP(http.StatusOK, gin.H{
            "code":    0,
            "message": "hi,dbkuaizi",
        })
    })
    App.Run(":2333")
}

利用 NoRoute 去掉 /static 目录

上面我们虽然实现了静态资源的加载,但为了区分 api 接口和静态资源,不得不多加一层 /static 静态资源目录,其实有更巧妙的方法免除掉这个目录。

我们可以利用 gin.NoRoute() 方法,这个方法的本意是当请求的接口不存在时,用于处理 404 相关的操作。我们可以在接口匹配失败后尝试加载静态资源,请求地址如下:

package main

import (
    "embed"
    "github.com/gin-gonic/gin"
    "io/fs"
    "net/http"
)

var (
    //go:embed web/*
    WebDir embed.FS
    App    *gin.Engine
)

func main() {
    App = gin.New()
    // 这是一个接口
    App.GET("/api", func(context *gin.Context) {
        context.JSONP(http.StatusOK, gin.H{
            "code":    0,
            "message": "hi,dbkuaizi",
        })
    })
    // 嵌入的 embed 就是一个文件系统,获取 web 目录下的资源文件
    staticFp, _ := fs.Sub(WebDir, "web")
    // 所有请求先匹配 api 路由,如果没匹配到就当作静态资源文件处理
    App.NoRoute(gin.WrapH(http.FileServer(http.FS(staticFp))))
    // 实现 api 端口、服务器端口共用一个
    App.Run(":2333")
}

在使用引用静态文件时,将 Web 目录当作根目录来使用,例如:

<!doctype html>
<html lang="zh">
<head>
    <title>dbkuaizi.com</title>
</head>
<body>
    <h1>hi,dbkuaizi</h1>
        <img src="./photo.jpg" width="200px">
    </body>
</html>

最终效果:
Snipaste_2024-05-18_20-42-05.webp
Snipaste_2024-05-18_20-42-16.webp

Leave a Comment

已有 1 条评论
  1. 这篇文章写得深入浅出,让我这个小白也看懂了!