package cache import ( "crypto/md5" "fmt" "os" "sync" "time" ) type FileCache struct { mu sync.Mutex locks map[string]*sync.Mutex Dir string } func (fc *FileCache) getLock(key string) *sync.Mutex { fc.mu.Lock() defer fc.mu.Unlock() if fc.locks == nil { fc.locks = make(map[string]*sync.Mutex) } if _, ok := fc.locks[key]; !ok { fc.locks[key] = new(sync.Mutex) } return fc.locks[key] } func (c *FileCache) Get(key string, value interface{}) error { data, _ := c.getValue(key) err := DecodeValue(data, value) return err } // get value; if the file does not exist or has expired, return empty and ignore errors func (c *FileCache) getValue(key string) (string, error) { f := c.fileName(key) fileInfo, err := os.Stat(f) if err != nil { // file does not exist return "", nil } difT := time.Now().Sub(fileInfo.ModTime()) if difT >= 0 { os.Remove(f) return "", nil } data, err := os.ReadFile(f) if err != nil { return "", nil } return string(data), nil } // save value func (c *FileCache) saveValue(key string, value string, exp int) error { f := c.fileName(key) lock := c.getLock(f) lock.Lock() defer lock.Unlock() err := os.WriteFile(f, ([]byte)(value), 0644) if err != nil { return err } if exp <= 0 { exp = MaxTimeOut } expFromNow := time.Now().Add(time.Duration(exp) * time.Second) err = os.Chtimes(f, expFromNow, expFromNow) return err } func (c *FileCache) Set(key string, value interface{}, exp int) error { str, err := EncodeValue(value) if err != nil { return err } err = c.saveValue(key, str, exp) return err } func (c *FileCache) SetDir(path string) { c.Dir = path } func (c *FileCache) fileName(key string) string { f := c.Dir + string(os.PathSeparator) + fmt.Sprintf("%x", md5.Sum([]byte(key))) return f } func (c *FileCache) Gc() error { // check file expiration time and delete return nil } func NewFileCache() *FileCache { return &FileCache{ locks: make(map[string]*sync.Mutex), Dir: os.TempDir(), } }