2025-12-04 18:45:25 +08:00
|
|
|
|
package file
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
2025-12-07 16:25:37 +08:00
|
|
|
|
"io/fs"
|
2025-12-04 18:45:25 +08:00
|
|
|
|
"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 的回调中使用。
|
2025-12-05 16:31:12 +08:00
|
|
|
|
func CreateTempDir(subDir string) (string, error) {
|
|
|
|
|
|
fullDirPath := filepath.Join(instance.tempRoot, subDir)
|
2025-12-04 18:45:25 +08:00
|
|
|
|
if err := os.MkdirAll(fullDirPath, 0755); err != nil {
|
|
|
|
|
|
return "", fmt.Errorf("创建临时目录 %s 失败: %w", fullDirPath, err)
|
|
|
|
|
|
}
|
|
|
|
|
|
return fullDirPath, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PrepareTempFilePath 获取文件在指定子目录下的完整路径,并确保父目录存在。
|
|
|
|
|
|
// 注意:此函数本身不是线程安全的,应在 ExecuteWithLock 的回调中使用。
|
2025-12-05 16:31:12 +08:00
|
|
|
|
func PrepareTempFilePath(subDir, fileName string) (string, error) {
|
|
|
|
|
|
fullDirPath := filepath.Join(instance.tempRoot, subDir)
|
2025-12-04 18:45:25 +08:00
|
|
|
|
if err := os.MkdirAll(fullDirPath, 0755); err != nil {
|
|
|
|
|
|
return "", fmt.Errorf("创建临时文件父目录 %s 失败: %w", fullDirPath, err)
|
|
|
|
|
|
}
|
|
|
|
|
|
return filepath.Join(fullDirPath, fileName), nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// RemoveTempDir 在指定的根目录下清理并删除一个子目录。
|
|
|
|
|
|
// 注意:此函数本身不是线程安全的,应在 ExecuteWithLock 的回调中使用。
|
2025-12-05 16:31:12 +08:00
|
|
|
|
func RemoveTempDir(subDir string) error {
|
|
|
|
|
|
fullDirPath := filepath.Join(instance.tempRoot, subDir)
|
2025-12-04 18:45:25 +08:00
|
|
|
|
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 的回调中使用。
|
2025-12-05 16:31:12 +08:00
|
|
|
|
func WriteTempFile(subDir, fileName string, data []byte) (string, error) {
|
|
|
|
|
|
fullDirPath := filepath.Join(instance.tempRoot, subDir)
|
2025-12-04 18:45:25 +08:00
|
|
|
|
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 的回调中使用。
|
2025-12-05 16:31:12 +08:00
|
|
|
|
func ReadTempFile(subDir, fileName string) ([]byte, error) {
|
|
|
|
|
|
filePath := filepath.Join(instance.tempRoot, subDir, fileName)
|
2025-12-04 18:45:25 +08:00
|
|
|
|
data, err := os.ReadFile(filePath)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("读取临时文件 %s 失败: %w", filePath, err)
|
|
|
|
|
|
}
|
|
|
|
|
|
return data, nil
|
|
|
|
|
|
}
|
2025-12-07 16:25:37 +08:00
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
|
}
|