如何使用go:embed嵌入配置文件供viper正常加载应用属性
问题原因
你当前代码不生效的核心原因是:viper.AddConfigPath("./configs")默认读取的是操作系统本地文件系统的路径,完全没有用到你通过//go:embed嵌入的embed.FS资源,所以运行时只会去二进制同级的磁盘目录找configs文件夹,自然读不到嵌入到二进制内部的配置文件。
正确解决方案
首先确认前置条件:
- 你的
//go:embed指令写在Go源文件的顶层作用域(不能写在函数内部) - embed路径是相对于当前Go源文件的正确相对路径,不包含
..等跨目录特殊写法、不以/开头 - 使用的viper版本 ≥ v1.10.0(低版本没有嵌入FS支持,用后面的通用写法即可)
写法1:使用viper原生的FS支持(推荐)
直接替换原来的配置加载逻辑,通过viper.AddConfigFS传入你嵌入的文件系统,注意路径要对应embed.FS内部的路径规则:
package main import ( "embed" "github.com/spf13/viper" ) //go:embed configs/*.conf var resources embed.FS func LoadConfig() error { // 传入嵌入的文件系统,后续配置路径都基于该FS查找 viper.AddConfigFS(resources) // 注意:这里写embed.FS内的目录路径,不要加./前缀 viper.AddConfigPath("configs") // 配置文件名不需要带后缀 viper.SetConfigName("app_dev") // 显式指定配置格式,避免.conf后缀无法被viper自动识别 viper.SetConfigType("properties") if err := viper.ReadInConfig(); err != nil { return err } return nil }
写法2:直接读取嵌入文件(通用,无viper版本要求)
如果你的viper版本较低,或者不需要多配置文件自动查找能力,可以直接打开嵌入的配置文件,交给viper解析,完全不会有路径匹配问题:
func LoadConfig() error { // 直接打开嵌入的目标配置文件,路径写embed内的完整相对路径 confFile, err := resources.Open("configs/app_dev.conf") if err != nil { return err } defer confFile.Close() viper.SetConfigType("properties") // 直接从打开的文件句柄读取配置 if err := viper.ReadConfig(confFile); err != nil { return err } return nil }
常见踩坑点
- 不要保留原来的
viper.AddConfigPath("./configs")代码:调用AddConfigFS后,后续传入的AddConfigPath是相对于嵌入FS的路径,写本地磁盘路径会导致查找失败 - 如果你的配置文件不是properties格式(比如是yaml、toml),把
SetConfigType的参数改成对应的格式(yaml/toml)即可 - 编译后直接运行二进制即可,不需要在二进制同级放置configs文件夹,所有匹配
configs/*.conf的文件已经被打包进二进制内部 - 如果写embed指令的Go源文件和configs目录不在同一层级,要调整embed的相对路径,比如源文件在
cmd/server/main.go,configs在项目根,那embed路径要写成//go:embed ../../configs/*.conf
内容的提问来源于stack exchange,提问作者Rocky
相关产品推荐
相关产品推荐

