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

2 回复

为什么不使用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配置文件保持你原有的格式不变。

回到顶部