Files

151 lines
5.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package file
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"sync"
"gopkg.in/yaml.v2"
)
// manager 是文件管理器的内部实现结构体,用于管理临时文件根路径和提供并发安全。
type manager struct {
tempRoot string // 临时文件存储的根路径
mu sync.Mutex // 用于保证文件操作的线程安全
}
var (
instance *manager
once sync.Once
)
// init 在包被导入时自动执行,用于初始化文件管理器单例。
func init() {
once.Do(func() {
instance = &manager{
tempRoot: "./tmp", // 默认的临时文件存储根路径
}
})
}
// SetTempRoot 设置临时文件存储的根目录。
// 此函数是线程安全的。
func SetTempRoot(path string) {
instance.mu.Lock()
defer instance.mu.Unlock()
instance.tempRoot = path
}
// ExecuteWithLock 提供一个通用的、带回滚能力的事务性执行单元。
// 它会获取一个全局锁,然后执行 action。如果 action 执行出错,它将调用 onRollback。
//
// action: 需要以原子方式执行的操作。
// onRollback: 当 action 返回错误时执行的回滚操作,原始错误会传入其中。
func ExecuteWithLock(action func() error, onRollback func(err error)) error {
instance.mu.Lock()
defer instance.mu.Unlock()
err := action()
if err != nil && onRollback != nil {
onRollback(err)
}
return err
}
// CreateTempDir 在指定的根目录下创建一个子目录。
// 注意:此函数本身不是线程安全的,应在 ExecuteWithLock 的回调中使用。
func CreateTempDir(subDir string) (string, error) {
fullDirPath := filepath.Join(instance.tempRoot, subDir)
if err := os.MkdirAll(fullDirPath, 0755); err != nil {
return "", fmt.Errorf("创建临时目录 %s 失败: %w", fullDirPath, err)
}
return fullDirPath, nil
}
// PrepareTempFilePath 获取文件在指定子目录下的完整路径,并确保父目录存在。
// 注意:此函数本身不是线程安全的,应在 ExecuteWithLock 的回调中使用。
func PrepareTempFilePath(subDir, fileName string) (string, error) {
fullDirPath := filepath.Join(instance.tempRoot, subDir)
if err := os.MkdirAll(fullDirPath, 0755); err != nil {
return "", fmt.Errorf("创建临时文件父目录 %s 失败: %w", fullDirPath, err)
}
return filepath.Join(fullDirPath, fileName), nil
}
// RemoveTempDir 在指定的根目录下清理并删除一个子目录。
// 注意:此函数本身不是线程安全的,应在 ExecuteWithLock 的回调中使用。
func RemoveTempDir(subDir string) error {
fullDirPath := filepath.Join(instance.tempRoot, subDir)
return os.RemoveAll(fullDirPath)
}
// RemoveDir 清理并删除指定的任意目录。
// 此函数与临时文件管理器无关,本身是线程安全的(依赖于操作系统的原子性)。
func RemoveDir(dirPath string) error {
return os.RemoveAll(dirPath)
}
// LoadYaml 从指定路径加载并解析一个 YAML 文件到传入的结构体中。
// path: YAML 文件的路径。
// c: 目标结构体的指针,例如 &MyConfig{}。
func LoadYaml(path string, c interface{}) error {
// 读取配置文件
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("配置文件读取失败: %w", err)
}
// 解析YAML配置
if err := yaml.Unmarshal(data, c); err != nil {
return fmt.Errorf("配置文件解析失败: %w", err)
}
return nil
}
// WriteTempFile 将数据写入到临时子目录的指定文件中。
// 它会自动创建所需的子目录。
// 注意:此函数本身不是线程安全的,应在 ExecuteWithLock 的回调中使用。
func WriteTempFile(subDir, fileName string, data []byte) (string, error) {
fullDirPath := filepath.Join(instance.tempRoot, subDir)
if err := os.MkdirAll(fullDirPath, 0755); err != nil {
return "", fmt.Errorf("为写入操作创建临时目录 %s 失败: %w", fullDirPath, err)
}
filePath := filepath.Join(fullDirPath, fileName)
if err := os.WriteFile(filePath, data, 0644); err != nil {
return "", fmt.Errorf("写入临时文件 %s 失败: %w", filePath, err)
}
return filePath, nil
}
// ReadTempFile 从临时子目录的指定文件中读取数据。
// 注意:此函数本身不是线程安全的,应在 ExecuteWithLock 的回调中使用。
func ReadTempFile(subDir, fileName string) ([]byte, error) {
filePath := filepath.Join(instance.tempRoot, subDir, fileName)
data, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("读取临时文件 %s 失败: %w", filePath, err)
}
return data, nil
}
// WalkTempDir 遍历指定的临时子目录。
// 它会自动处理根路径的拼接,并调用标准库的 filepath.WalkDir。
// subDir: 要遍历的子目录。
// fn: 应用于每个文件和目录的回调函数。
func WalkTempDir(subDir string, fn fs.WalkDirFunc) error {
root := filepath.Join(instance.tempRoot, subDir)
return filepath.WalkDir(root, fn)
}
// GetRelativePathInTemp 将一个在临时目录中的绝对路径,转换为相对于指定的子目录的相对路径。
// 例如absolutePath="C:\tmp\ota\123\lib\a.py", subDir="ota/123" -> 返回 "lib\a.py"
func GetRelativePathInTemp(absolutePath string, subDir string) (string, error) {
basePath := filepath.Join(instance.tempRoot, subDir)
return filepath.Rel(basePath, absolutePath)
}