package common import ( "fmt" "os" "path/filepath" "time" "github.com/google/uuid" ) // DiskCacheType 磁盘缓存类型 type DiskCacheType string const ( DiskCacheTypeBody DiskCacheType = "body" // 请求体缓存 DiskCacheTypeFile DiskCacheType = "file" // 文件数据缓存 ) // 统一的缓存目录名 const diskCacheDir = "new-api-body-cache" // GetDiskCacheDir 获取统一的磁盘缓存目录 // 注意:每次调用都会重新计算,以响应配置变化 func GetDiskCacheDir() string { cachePath := GetDiskCachePath() if cachePath == "" { cachePath = os.TempDir() } return filepath.Join(cachePath, diskCacheDir) } // EnsureDiskCacheDir 确保缓存目录存在 func EnsureDiskCacheDir() error { dir := GetDiskCacheDir() return os.MkdirAll(dir, 0755) } // CreateDiskCacheFile 创建磁盘缓存文件 // cacheType: 缓存类型(body/file) // 返回文件路径和文件句柄 func CreateDiskCacheFile(cacheType DiskCacheType) (string, *os.File, error) { if err := EnsureDiskCacheDir(); err != nil { return "", nil, fmt.Errorf("failed to create cache directory: %w", err) } dir := GetDiskCacheDir() filename := fmt.Sprintf("%s-%s-%d.tmp", cacheType, uuid.New().String()[:8], time.Now().UnixNano()) filePath := filepath.Join(dir, filename) file, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR|os.O_EXCL, 0600) if err != nil { return "", nil, fmt.Errorf("failed to create cache file: %w", err) } return filePath, file, nil } // WriteDiskCacheFile 写入数据到磁盘缓存文件 // 返回文件路径 func WriteDiskCacheFile(cacheType DiskCacheType, data []byte) (string, error) { filePath, file, err := CreateDiskCacheFile(cacheType) if err != nil { return "", err } _, err = file.Write(data) if err != nil { file.Close() os.Remove(filePath) return "", fmt.Errorf("failed to write cache file: %w", err) } if err := file.Close(); err != nil { os.Remove(filePath) return "", fmt.Errorf("failed to close cache file: %w", err) } return filePath, nil } // WriteDiskCacheFileString 写入字符串到磁盘缓存文件 func WriteDiskCacheFileString(cacheType DiskCacheType, data string) (string, error) { return WriteDiskCacheFile(cacheType, []byte(data)) } // ReadDiskCacheFile 读取磁盘缓存文件 func ReadDiskCacheFile(filePath string) ([]byte, error) { return os.ReadFile(filePath) } // ReadDiskCacheFileString 读取磁盘缓存文件为字符串 func ReadDiskCacheFileString(filePath string) (string, error) { data, err := os.ReadFile(filePath) if err != nil { return "", err } return string(data), nil } // RemoveDiskCacheFile 删除磁盘缓存文件 func RemoveDiskCacheFile(filePath string) error { return os.Remove(filePath) } // CleanupOldDiskCacheFiles 清理旧的缓存文件 // maxAge: 文件最大存活时间 // 注意:此函数只删除文件,不更新统计(因为无法知道每个文件的原始大小) func CleanupOldDiskCacheFiles(maxAge time.Duration) error { dir := GetDiskCacheDir() entries, err := os.ReadDir(dir) if err != nil { if os.IsNotExist(err) { return nil // 目录不存在,无需清理 } return err } now := time.Now() for _, entry := range entries { if entry.IsDir() { continue } info, err := entry.Info() if err != nil { continue } if now.Sub(info.ModTime()) > maxAge { // 注意:后台清理任务删除文件时,由于无法得知原始 base64Size, // 只能按磁盘文件大小扣减。这在目前 base64 存储模式下是准确的。 if err := os.Remove(filepath.Join(dir, entry.Name())); err == nil { DecrementDiskFiles(info.Size()) } } } return nil } // GetDiskCacheInfo 获取磁盘缓存目录信息 func GetDiskCacheInfo() (fileCount int, totalSize int64, err error) { dir := GetDiskCacheDir() entries, err := os.ReadDir(dir) if err != nil { if os.IsNotExist(err) { return 0, 0, nil } return 0, 0, err } for _, entry := range entries { if entry.IsDir() { continue } info, err := entry.Info() if err != nil { continue } fileCount++ totalSize += info.Size() } return fileCount, totalSize, nil } // ShouldUseDiskCache 判断是否应该使用磁盘缓存 func ShouldUseDiskCache(dataSize int64) bool { if !IsDiskCacheEnabled() { return false } threshold := GetDiskCacheThresholdBytes() if dataSize < threshold { return false } return IsDiskCacheAvailable(dataSize) }