Golang中如何处理动态YAML配置文件
Golang中如何处理动态YAML配置文件 我创建了一个应用程序,用于操作特定文件夹中的某些文件,首先在本地移动它们,操作后再将它们移回服务器上的原始文件夹。
该应用程序将在多台机器上运行,每台机器“监控”一组特定的文件夹,有些机器只监控1个文件夹,有些则监控2个或更多文件夹。
我使用一个YAML配置文件来传递每个要监控的文件夹路径。YAML文件(config.yml)如下所示:
# 此文件夹临时用于执行操作
processingfolder: C:\Processing\
# 服务器文件夹,它们都映射到本地计算机上,或使用地址
drives:
# folder1
folder1: F:\data\
# folder2
folder2: G:\data\
# folder3
folder3: H:\data\
为了解析配置文件,我会这样做:
package main
import (
"io/ioutil"
"os"
"gopkg.in/yaml.v2"
)
//configuration from .yml file
type config struct {
processingfolder string `yaml:"processingfolder"`
drives struct {
folder1 string `yaml:"folder1"`
folder2 string `yaml:"folder2"`
folder3 string `yaml:"folder3"`
} `yaml:"drives"`
}
func (c *config) getConf() *config {
yamlFile, err := ioutil.ReadFile("./config.yml")
if err != nil {
//file does not exist
os.Exit(1)
}
err = yaml.Unmarshal(yamlFile, c)
if err != nil {
//wrong format
os.Exit(1)
}
if _, err := os.Stat(c.processingfolder); os.IsNotExist(err) {
//it should exist tho
os.Exit(1)
}
if _, err := os.Stat(c.drives.folder1); os.IsNotExist(err) {
//does not exist, tell to check mapping
os.Exit(1)
}
if _, err := os.Stat(c.drives.folder2); os.IsNotExist(err) {
//does not exist, tell to check mapping
os.Exit(1)
}
if _, err := os.Stat(c.drives.folder3); os.IsNotExist(err) {
//does not exist, tell to check mapping
os.Exit(1)
}
return c
}
func main() {
var c config
c.getConf()
//do stuff here
}
现在问题来了。该应用程序将被多台机器使用,有些只需要监控一个文件夹,有些则需要监控两个或更多。要监控的文件夹数量不是固定的,而且很可能会不时变化。此外,也不知道一台机器需要监控的最大文件夹数量是多少,可能是10个,可能是20个,甚至更多。我可以手动编写配置结构体来容纳30多个驱动器:
type config struct {
processingfolder string `yaml:"processingfolder"`
drives struct {
folder1 string `yaml:"folder1"`
folder2 string `yaml:"folder2"`
//...
//...
folder30 string `yaml:"folder30"`
} `yaml:"drives"`
}
并让应用程序只使用YAML文件中“指定”的那些文件夹,但我觉得这样做不太优雅。
有没有办法动态创建配置结构体?
更多关于Golang中如何处理动态YAML配置文件的实战教程也可以访问 https://www.itying.com/category-94-b0.html
为什么不使用YAML文件中的集合?https://yaml.org/spec/1.2/spec.html#id2759963
更多关于Golang中如何处理动态YAML配置文件的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中处理动态YAML配置,可以使用map[string]interface{}或自定义结构体配合切片。以下是两种解决方案:
方案1:使用map动态解析
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"gopkg.in/yaml.v2"
)
type Config struct {
ProcessingFolder string `yaml:"processingfolder"`
Drives map[string]string `yaml:"drives"`
}
func (c *Config) LoadConfig(filePath string) error {
yamlFile, err := ioutil.ReadFile(filePath)
if err != nil {
return fmt.Errorf("读取配置文件失败: %v", err)
}
err = yaml.Unmarshal(yamlFile, c)
if err != nil {
return fmt.Errorf("解析YAML失败: %v", err)
}
// 验证处理文件夹
if _, err := os.Stat(c.ProcessingFolder); os.IsNotExist(err) {
return fmt.Errorf("处理文件夹不存在: %s", c.ProcessingFolder)
}
// 验证所有驱动器文件夹
for name, path := range c.Drives {
if _, err := os.Stat(path); os.IsNotExist(err) {
return fmt.Errorf("驱动器文件夹不存在 [%s]: %s", name, path)
}
}
return nil
}
func main() {
var config Config
err := config.LoadConfig("./config.yml")
if err != nil {
fmt.Printf("配置加载失败: %v\n", err)
os.Exit(1)
}
fmt.Printf("处理文件夹: %s\n", config.ProcessingFolder)
fmt.Println("监控的驱动器:")
for name, path := range config.Drives {
fmt.Printf(" %s: %s\n", name, path)
}
// 示例:处理所有文件夹
for folderName, folderPath := range config.Drives {
fmt.Printf("开始处理文件夹 %s: %s\n", folderName, folderPath)
// 这里添加你的文件夹处理逻辑
}
}
方案2:使用切片存储文件夹配置
package main
import (
"fmt"
"io/ioutil"
"os"
"gopkg.in/yaml.v2"
)
type FolderConfig struct {
Name string `yaml:"name"`
Path string `yaml:"path"`
}
type Config struct {
ProcessingFolder string `yaml:"processingfolder"`
Drives []FolderConfig `yaml:"drives"`
}
func (c *Config) LoadConfig(filePath string) error {
yamlFile, err := ioutil.ReadFile(filePath)
if err != nil {
return fmt.Errorf("读取配置文件失败: %v", err)
}
err = yaml.Unmarshal(yamlFile, c)
if err != nil {
return fmt.Errorf("解析YAML失败: %v", err)
}
if _, err := os.Stat(c.ProcessingFolder); os.IsNotExist(err) {
return fmt.Errorf("处理文件夹不存在: %s", c.ProcessingFolder)
}
for _, drive := range c.Drives {
if _, err := os.Stat(drive.Path); os.IsNotExist(err) {
return fmt.Errorf("文件夹不存在 [%s]: %s", drive.Name, drive.Path)
}
}
return nil
}
func main() {
var config Config
err := config.LoadConfig("./config.yml")
if err != nil {
fmt.Printf("配置加载失败: %v\n", err)
os.Exit(1)
}
fmt.Printf("处理文件夹: %s\n", config.ProcessingFolder)
fmt.Println("监控的文件夹:")
for _, drive := range config.Drives {
fmt.Printf(" %s: %s\n", drive.Name, drive.Path)
}
}
对应的YAML配置文件:
processingfolder: C:\Processing\
drives:
- name: folder1
path: F:\data\
- name: folder2
path: G:\data\
- name: folder3
path: H:\data\
方案3:混合模式(支持命名和顺序访问)
package main
import (
"fmt"
"io/ioutil"
"os"
"gopkg.in/yaml.v2"
)
type Config struct {
ProcessingFolder string `yaml:"processingfolder"`
Drives map[string]string `yaml:"drives"`
DriveList []string `yaml:"-"` // 从Drives map派生
}
func (c *Config) LoadConfig(filePath string) error {
yamlFile, err := ioutil.ReadFile(filePath)
if err != nil {
return fmt.Errorf("读取配置文件失败: %v", err)
}
err = yaml.Unmarshal(yamlFile, c)
if err != nil {
return fmt.Errorf("解析YAML失败: %v", err)
}
if _, err := os.Stat(c.ProcessingFolder); os.IsNotExist(err) {
return fmt.Errorf("处理文件夹不存在: %s", c.ProcessingFolder)
}
// 构建文件夹列表
c.DriveList = make([]string, 0, len(c.Drives))
for name, path := range c.Drives {
if _, err := os.Stat(path); os.IsNotExist(err) {
return fmt.Errorf("文件夹不存在 [%s]: %s", name, path)
}
c.DriveList = append(c.DriveList, name)
}
return nil
}
func (c *Config) GetDrivePath(name string) (string, bool) {
path, exists := c.Drives[name]
return path, exists
}
func (c *Config) GetDriveByIndex(index int) (string, string, bool) {
if index < 0 || index >= len(c.DriveList) {
return "", "", false
}
name := c.DriveList[index]
path := c.Drives[name]
return name, path, true
}
func main() {
var config Config
err := config.LoadConfig("./config.yml")
if err != nil {
fmt.Printf("配置加载失败: %v\n", err)
os.Exit(1)
}
// 按名称访问
if path, exists := config.GetDrivePath("folder1"); exists {
fmt.Printf("folder1路径: %s\n", path)
}
// 按索引遍历
for i := 0; i < len(config.DriveList); i++ {
name, path, exists := config.GetDriveByIndex(i)
if exists {
fmt.Printf("文件夹 %d: %s -> %s\n", i, name, path)
}
}
}
使用map的方案最灵活,可以动态处理任意数量的文件夹,无需预定义结构体字段。YAML配置文件保持你原有的格式不变。

