mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2025-11-25 03:15:19 +08:00
* feat(cache): improve cache management * feat(disk-usage): add cache * feat(disk-usage): add refresh * fix(disk-usage): cache with ttl * feat(cache): implement KeyedCache and TypedCache for improved caching mechanism * fix(copy): update object retrieval to use Get instead of GetUnwrap * refactor(cache): simplify DirectoryCache structure and improve object management * fix(cache): correct cache entry initialization and key deletion logic in TypedCache * refactor(driver): remove GetObjInfo interface and simplify Link function logic https://github.com/OpenListTeam/OpenList/pull/888/files#r2430925783 * fix(link): optimize link retrieval and caching logic * refactor(cache): consolidate cache management and improve directory cache handling * fix(cache): add cache control based on storage configuration in List function * . * refactor: replace fmt.Sprintf with strconv for integer conversions * refactor(cache): enhance cache entry management with Expirable interface * fix(cache): improve link reference acquisition logic to handle expiration * refactor: replace OnlyLinkMFile with NoLinkSF in driver configurations and logic * refactor(link): enhance link caching logic with dynamic type keys based on IP and User-Agent * feat(drivers): add LinkCacheType to driver configurations for enhanced caching * refactor(cache): streamline directory object management in cache operations * refactor(cache): remove unnecessary 'dirty' field from CacheEntry structure * refactor(cache): replace 'dirty' field with bitwise flags * refactor(io): 调高SyncClosers.AcquireReference的优先级 * refactor(link): 优化链接获取逻辑,增加重 * refactor(link): 添加RequireReference字段以增强链接管理 * refactor(link): 移除MFile字段,改用RangeReader * refactor: 移除不必要的NoLinkSF字段 * refactor(cache): 修改目录缓存的脏标志定义和更新逻辑 * feat(cache): add expiration gc --------- Co-authored-by: KirCute <951206789@qq.com> Co-authored-by: KirCute <kircute@foxmail.com> Co-authored-by: j2rong4cn <j2rong@qq.com>
188 lines
4.3 KiB
Go
188 lines
4.3 KiB
Go
package strm
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
stdpath "path"
|
||
"strings"
|
||
|
||
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
||
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
||
"github.com/OpenListTeam/OpenList/v4/internal/fs"
|
||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||
"github.com/OpenListTeam/OpenList/v4/internal/sign"
|
||
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||
"github.com/OpenListTeam/OpenList/v4/server/common"
|
||
)
|
||
|
||
type Strm struct {
|
||
model.Storage
|
||
Addition
|
||
pathMap map[string][]string
|
||
autoFlatten bool
|
||
oneKey string
|
||
|
||
supportSuffix map[string]struct{}
|
||
downloadSuffix map[string]struct{}
|
||
}
|
||
|
||
func (d *Strm) Config() driver.Config {
|
||
return config
|
||
}
|
||
|
||
func (d *Strm) GetAddition() driver.Additional {
|
||
return &d.Addition
|
||
}
|
||
|
||
func (d *Strm) Init(ctx context.Context) error {
|
||
if d.Paths == "" {
|
||
return errors.New("paths is required")
|
||
}
|
||
d.pathMap = make(map[string][]string)
|
||
for _, path := range strings.Split(d.Paths, "\n") {
|
||
path = strings.TrimSpace(path)
|
||
if path == "" {
|
||
continue
|
||
}
|
||
k, v := getPair(path)
|
||
d.pathMap[k] = append(d.pathMap[k], v)
|
||
}
|
||
if len(d.pathMap) == 1 {
|
||
for k := range d.pathMap {
|
||
d.oneKey = k
|
||
}
|
||
d.autoFlatten = true
|
||
} else {
|
||
d.oneKey = ""
|
||
d.autoFlatten = false
|
||
}
|
||
|
||
d.supportSuffix = supportSuffix()
|
||
if d.FilterFileTypes != "" {
|
||
types := strings.Split(d.FilterFileTypes, ",")
|
||
for _, ext := range types {
|
||
ext = strings.ToLower(strings.TrimSpace(ext))
|
||
if ext != "" {
|
||
d.supportSuffix[ext] = struct{}{}
|
||
}
|
||
}
|
||
}
|
||
|
||
d.downloadSuffix = downloadSuffix()
|
||
if d.DownloadFileTypes != "" {
|
||
downloadTypes := strings.Split(d.DownloadFileTypes, ",")
|
||
for _, ext := range downloadTypes {
|
||
ext = strings.ToLower(strings.TrimSpace(ext))
|
||
if ext != "" {
|
||
d.downloadSuffix[ext] = struct{}{}
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (d *Strm) Drop(ctx context.Context) error {
|
||
d.pathMap = nil
|
||
d.downloadSuffix = nil
|
||
d.supportSuffix = nil
|
||
return nil
|
||
}
|
||
|
||
func (d *Strm) Get(ctx context.Context, path string) (model.Obj, error) {
|
||
if utils.PathEqual(path, "/") {
|
||
return &model.Object{
|
||
Name: "Root",
|
||
IsFolder: true,
|
||
Path: "/",
|
||
}, nil
|
||
}
|
||
root, sub := d.getRootAndPath(path)
|
||
dsts, ok := d.pathMap[root]
|
||
if !ok {
|
||
return nil, errs.ObjectNotFound
|
||
}
|
||
for _, dst := range dsts {
|
||
reqPath := stdpath.Join(dst, sub)
|
||
obj, err := fs.Get(ctx, reqPath, &fs.GetArgs{NoLog: true})
|
||
if err != nil {
|
||
continue
|
||
}
|
||
// fs.Get 没报错,说明不是strm驱动映射的路径,需要直接返回
|
||
size := int64(0)
|
||
if !obj.IsDir() {
|
||
size = obj.GetSize()
|
||
path = reqPath //把路径设置为真实的,供Link直接读取
|
||
}
|
||
return &model.Object{
|
||
Path: path,
|
||
Name: obj.GetName(),
|
||
Size: size,
|
||
Modified: obj.ModTime(),
|
||
IsFolder: obj.IsDir(),
|
||
HashInfo: obj.GetHash(),
|
||
}, nil
|
||
}
|
||
if strings.HasSuffix(path, ".strm") {
|
||
// 上面fs.Get都没找到且后缀为.strm
|
||
// 返回errs.NotSupport使得op.Get尝试从op.List中查找
|
||
return nil, errs.NotSupport
|
||
}
|
||
return nil, errs.ObjectNotFound
|
||
}
|
||
|
||
func (d *Strm) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||
path := dir.GetPath()
|
||
if utils.PathEqual(path, "/") && !d.autoFlatten {
|
||
return d.listRoot(), nil
|
||
}
|
||
root, sub := d.getRootAndPath(path)
|
||
dsts, ok := d.pathMap[root]
|
||
if !ok {
|
||
return nil, errs.ObjectNotFound
|
||
}
|
||
var objs []model.Obj
|
||
fsArgs := &fs.ListArgs{NoLog: true, Refresh: args.Refresh}
|
||
for _, dst := range dsts {
|
||
tmp, err := d.list(ctx, dst, sub, fsArgs)
|
||
if err == nil {
|
||
objs = append(objs, tmp...)
|
||
}
|
||
}
|
||
return objs, nil
|
||
}
|
||
|
||
func (d *Strm) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||
if file.GetID() == "strm" {
|
||
link := d.getLink(ctx, file.GetPath())
|
||
return &model.Link{
|
||
RangeReader: stream.GetRangeReaderFromMFile(int64(len(link)), strings.NewReader(link)),
|
||
}, nil
|
||
}
|
||
// ftp,s3
|
||
if common.GetApiUrl(ctx) == "" {
|
||
args.Redirect = false
|
||
}
|
||
reqPath := file.GetPath()
|
||
link, _, err := d.link(ctx, reqPath, args)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
if link == nil {
|
||
return &model.Link{
|
||
URL: fmt.Sprintf("%s/p%s?sign=%s",
|
||
common.GetApiUrl(ctx),
|
||
utils.EncodePath(reqPath, true),
|
||
sign.Sign(reqPath)),
|
||
}, nil
|
||
}
|
||
|
||
resultLink := *link
|
||
resultLink.SyncClosers = utils.NewSyncClosers(link)
|
||
return &resultLink, nil
|
||
}
|
||
|
||
var _ driver.Driver = (*Strm)(nil)
|