1 Star 0 Fork 0

RACH / go-filecache

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
Cache.go 9.44 KB
一键复制 编辑 原始数据 按行查看 历史
RACH 提交于 2023-07-03 09:12 . update Cache.go.
package Filecache
import (
"bytes"
"encoding/binary"
"fmt"
"io/ioutil"
"os"
"runtime"
"sync"
"time"
"unsafe"
"github.com/fatih/color"
// "log"
"bufio"
"path/filepath"
)
var isecho = false
type Item struct {
Object interface{}
Expiration int64
}
type Cache struct {
*cache
// If this is confusing, see the comment at the bottom of New()
}
type cacheinfo struct {
expire int64
size int64
}
type cache struct {
prefix string
path string
path_levels int
ext string
expire int64
info cacheinfo
items map[string]Item
mu sync.RWMutex
onEvicted func(string, interface{})
janitor *janitor
}
type janitor struct {
Interval int64
stop chan bool
}
func (j *janitor) Run(c *cache) {
if j.Interval <= 0 {
return
}
ticker := time.NewTicker((time.Duration)(j.Interval))
for {
select {
case <-ticker.C:
c.DeleteExpired()
echo("DeleteExpired", c.path, j.Interval)
case <-j.stop:
ticker.Stop()
return
}
}
}
func stopJanitor(c *Cache) {
c.janitor.stop <- true
}
func runJanitor(c *cache, ci int64) {
j := &janitor{
Interval: ci * 1000 * 1000 * 1000,
stop: make(chan bool),
}
c.janitor = j
go j.Run(c)
}
func newCache(de int64, m map[string]Item) *cache {
if de == 0 {
de = -1
}
c := &cache{
expire: de,
items: m,
path_levels: 1,
path: "./cache",
ext: "o",
prefix: "cache",
}
return c
}
func (c *cache) DeleteExpired() {
c.mu.Lock()
c.Flush()
c.mu.Unlock()
}
func (c *cache) Set_path(v string) {
c.path = v
}
func (c *cache) Set_echo(v bool) {
isecho = v
}
func (c *cache) Set_pathlevel(v int) {
if v <= 5 {
c.path_levels = v
}
}
func (c *cache) Set_prefix(v string) {
c.prefix = v
}
func (c *cache) Set_ext(v string) {
c.ext = v
}
func Exists(path string) bool {
_, err := os.Stat(path) //os.Stat获取文件信息
if err != nil {
if os.IsExist(err) {
return true
}
return false
}
return true
}
func (c *cache) Flush() bool {
root := c.path
err := filepath.Walk(root,
func(path string, info os.FileInfo, err error) error {
if err == nil {
if !info.IsDir() {
c.IsExpire(path)
echo("Flush", path)
}
}
return nil
})
if err != nil {
return false
}
return true
}
func (c *cache) Clear() bool {
root := c.path
if c.expire == 0 {
return false
}
err := filepath.Walk(root,
func(path string, info os.FileInfo, err error) error {
if err == nil {
if !info.IsDir() {
os.Remove(path)
}
}
return nil
})
if err != nil {
return false
}
return true
}
func newCacheWithJanitor(de int64, ci int64, m map[string]Item) *Cache {
c := newCache(de, m)
// This trick ensures that the janitor goroutine (which--granted it
// was enabled--is running DeleteExpired on c forever) does not keep
// the returned C object from being garbage collected. When it is
// garbage collected, the finalizer stops the janitor goroutine, after
// which c can be collected.
C := &Cache{c}
if ci > 0 {
runJanitor(c, ci)
runtime.SetFinalizer(C, stopJanitor)
}
return C
}
// Return a new cache with a given default expiration duration and cleanup
// int64erval. If the expiration duration is less than one (or NoExpiration),
// the items in the cache never expire (by default), and must be deleted
// manually. If the cleanup int64erval is less than one, expired items are not
// deleted from the cache before calling c.DeleteExpired().
func NewCache(defaultExpiration, cleanupInterval int64) *Cache {
items := make(map[string]Item)
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
}
// 截取字符串,支持多字节字符
// start:起始下标,负数从从尾部开始,最后一个为-1
// length:截取长度,负数表示截取到末尾
func SubStr(str string, start int, length int) (result string) {
s := []rune(str)
total := len(s)
if total == 0 {
return
}
// 允许从尾部开始计算
if start < 0 {
start = total + start
if start < 0 {
return
}
}
if start > total {
return
}
// 到末尾
if length < 0 {
length = total
}
end := start + length
if end > total {
result = string(s[start:])
} else {
result = string(s[start:end])
}
return
}
func (c *cache) getpath(key []byte) string {
filename := *(*string)(unsafe.Pointer(&key))
// path:=fmt.Sprintf("%s/%s/%s/",c.path,SubStr(filename,0,2),SubStr(filename,2,2))
path := c.path
for n := 0; n < c.path_levels; n += 2 {
path = fmt.Sprintf("%s/%s", path, SubStr(filename, n, n+2))
}
isexist := IsDir(path)
if !isexist {
createDir(path)
echo(path)
}
return fmt.Sprintf("%s/%s_%s.%s", path, c.prefix, filename, c.ext)
}
func (c *cache) Push(key []byte, data []byte, expire int64) (err error) {
c.mu.Lock()
c.info.expire = expire
path := c.getpath(key)
err = append(path, data, &c.info)
c.mu.Unlock()
return err
}
func (c *cache) Set(key []byte, data []byte, expire int64) (err error) {
c.info.expire = expire
path := c.getpath(key)
err = write(path, data, &c.info)
return err
}
func (c *cache) Get(key []byte) (data []byte, _expire int64, err error) {
path := c.getpath(key)
if c.IsExpire(path) {
return nil, 0, nil
}
echo(path)
data, _expire, err = read(path)
return data, _expire, err
}
func (c *cache) Gethwnd(key []byte) (r *bufio.Reader, pos int64, err error) {
path := c.getpath(key)
r, pos, err = createhwnd(path)
return r, pos, err
}
func (c *cache) IsExpire(path string) bool {
filetime, err := GetFileModTime(path)
info, _ := getheader(path)
if err != nil {
return true
}
nowtime := time.Now().Unix()
timespan := nowtime - filetime
// fmt.Println("stat",timespan,info.expire,err)
if timespan >= info.expire && info.expire != 0 && c.expire != 0 {
os.Remove(path)
echo("expire", timespan, path)
return true
}
return false
}
const (
INFO int = iota + 1
DEBUG
WARN
ERROR
)
func echo(v ...interface{}) {
info(WARN, v...)
}
func info(level int, v ...interface{}) {
if isecho == false {
return
}
var c *color.Color
switch level {
case DEBUG:
c = color.New(color.FgWhite)
case ERROR:
c = color.New(color.FgRed)
case WARN:
c = color.New(color.FgYellow)
case INFO:
c = color.New(color.FgGreen)
}
c.Println(v...)
}
/**
判断文件是否存在
*/
func IsDir(fileAddr string) bool {
s, err := os.Stat(fileAddr)
if err != nil {
return false
}
return s.IsDir()
}
/**
创建文件夹
*/
func createDir(dirName string) bool {
err := os.MkdirAll(dirName, os.ModePerm)
if err != nil {
return false
}
return true
}
func IntToBytes(n int64) []byte {
data := int64(n)
bytebuf := bytes.NewBuffer([]byte{})
binary.Write(bytebuf, binary.BigEndian, data)
return bytebuf.Bytes()
}
func BytesToInt(bys []byte) int64 {
bytebuff := bytes.NewBuffer(bys)
var data int64
binary.Read(bytebuff, binary.BigEndian, &data)
return int64(data)
}
//BytesCombine 多个[]byte数组合并成一个[]byte
func BytesCombine(pBytes ...[]byte) []byte {
return bytes.Join(pBytes, []byte(""))
}
var headpos int64 = 8
//获取头信息
func getheader(fileName string) (info cacheinfo, err error) {
//读取文件
reader, pos, err := createhwnd(fileName)
if err != nil {
// fmt.Println("读取错误:", err)
return info, err
}
buffer := make([]byte, pos)
reader.Read(buffer)
info.expire = BytesToInt(buffer)
return info, err
}
func read(fileName string) (data []byte, expire int64, err error) {
//读取文件
data, err = ioutil.ReadFile(fileName)
if err != nil {
// fmt.Println("读取错误:", err)
return nil, 0, err
}
expire = BytesToInt(data[:headpos])
return data[headpos:], expire, err
}
func createhwnd(fileName string) (reader *bufio.Reader, pos int64, err error) {
// 创建句柄
fi, err := os.Open(fileName)
if err != nil {
return nil, 0, err
}
// 创建 Reader
r := bufio.NewReader(fi)
fi.Close()
return r, headpos, nil
}
func write(fileName string, data []byte, info *cacheinfo) (err error) {
var header []byte = IntToBytes(info.expire)
// 写入头信息
// fmt.Println("header", header, info.expire, len(header), unsafe.Sizeof(info.expire))
//写入文件
err = ioutil.WriteFile(fileName, BytesCombine(header, data), 0666)
if err != nil {
fmt.Println("写入错误:", err)
return err
}
return nil
}
func append(filename string, data []byte, info *cacheinfo) (err error) {
// 以追加模式打开文件,当文件不存在时生成文件
f, err := os.OpenFile(filename, os.O_RDWR|os.O_APPEND, 0666)
if err != nil {
err = write(filename, data, info)
if err != nil {
return err
}
return nil
}
defer func(f *os.File) {
err := f.Close()
if err != nil {
panic(err)
}
}(f)
// 写入文件
n, err := f.Write(data)
// 当 n != len(str) 时,返回非零错误
if err == nil && n != len(data) {
return err
}
// fmt.Println(filename,data)
return nil
}
//获取文件修改时间 返回unix时间戳
func GetFileModTime(path string) (time1 int64, err error) {
f, err := os.Open(path)
if err != nil {
return (int64)(time.Now().Unix()), err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return (int64)(time.Now().Unix()), err
}
return fi.ModTime().Unix(), nil
}
Go
1
https://gitee.com/hkingsoft_admin/go-filecache.git
git@gitee.com:hkingsoft_admin/go-filecache.git
hkingsoft_admin
go-filecache
go-filecache
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891