You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

144 lines
2.6 KiB

package cache
import (
"encoding/json"
"os"
"path/filepath"
"sync"
"time"
)
// fileCache 文件缓存实现
type fileCache struct {
mu sync.RWMutex
dir string
}
// NewFileCache 创建文件缓存实例
func NewFileCache(dir string) *fileCache {
return &fileCache{
dir: dir,
}
}
func (c *fileCache) Set(key string, value interface{}, ttl time.Duration) error {
c.mu.Lock()
defer c.mu.Unlock()
if err := os.MkdirAll(c.dir, 0755); err != nil {
return err
}
// 强制将 Value 转换为 []byte
var data []byte
switch v := value.(type) {
case string:
data = []byte(v)
case []byte:
data = v
default:
var err error
data, err = json.Marshal(v)
if err != nil {
return err
}
}
item := struct {
Data []byte `json:"data"`
ExpiresAt time.Time `json:"expires_at"`
}{
Data: data,
ExpiresAt: time.Now().Add(ttl),
}
encoded, err := json.Marshal(item)
if err != nil {
return err
}
filePath := filepath.Join(c.dir, key)
return os.WriteFile(filePath, encoded, 0644)
}
type FileCacheItem struct {
Data []byte `json:"data"`
ExpiresAt time.Time `json:"expires_at"`
}
func (c *fileCache) Get(key string) (interface{}, error) {
c.mu.RLock()
defer c.mu.RUnlock()
filePath := filepath.Join(c.dir, key)
data, err := os.ReadFile(filePath)
if err != nil {
if os.IsNotExist(err) {
return nil, ErrCacheMiss
}
return nil, err
}
var item FileCacheItem
if err := json.Unmarshal(data, &item); err != nil {
return nil, err
}
if item.ExpiresAt.Before(time.Now()) {
_ = os.Remove(filePath)
return nil, ErrCacheExpired
}
return item.Data, nil
}
func (c *fileCache) GetKey(key string, clientId string) (interface{}, error) {
return c.Get(key + ":" + clientId)
}
func (c *fileCache) Delete(key string) error {
c.mu.Lock()
defer c.mu.Unlock()
filePath := filepath.Join(c.dir, key)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil
}
return os.Remove(filePath)
}
func (c *fileCache) Clear() error {
c.mu.Lock()
defer c.mu.Unlock()
entries, err := os.ReadDir(c.dir)
if err != nil {
return err
}
for _, entry := range entries {
_ = os.Remove(filepath.Join(c.dir, entry.Name()))
}
return nil
}
func (c *fileCache) Exists(key string) (bool, error) {
c.mu.RLock()
defer c.mu.RUnlock()
filePath := filepath.Join(c.dir, key)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return false, nil
}
// 检查是否过期
val, err := c.Get(key)
if err == ErrCacheExpired {
return false, nil
}
return val != nil, err
}
// PathExists 检查路径是否存在
func PathExists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}