perf(lanzou): optimize the use of list cache (#2956)
* fix:local sort not cache * perf(lanzou): Optimize the use of list cache
This commit is contained in:
parent
99a186d01b
commit
2f19d4a834
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -108,18 +109,19 @@ func (d *LanZou) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exp := GetExpirationTime(dfile.Url)
|
||||||
return &model.Link{
|
return &model.Link{
|
||||||
URL: dfile.Url,
|
URL: dfile.Url,
|
||||||
Header: http.Header{
|
Header: http.Header{
|
||||||
"User-Agent": []string{base.UserAgent},
|
"User-Agent": []string{base.UserAgent},
|
||||||
},
|
},
|
||||||
|
Expiration: &exp,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *LanZou) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *LanZou) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
|
||||||
if d.IsCookie() {
|
if d.IsCookie() {
|
||||||
_, err := d.doupload(func(req *resty.Request) {
|
data, err := d.doupload(func(req *resty.Request) {
|
||||||
req.SetContext(ctx)
|
req.SetContext(ctx)
|
||||||
req.SetFormData(map[string]string{
|
req.SetFormData(map[string]string{
|
||||||
"task": "2",
|
"task": "2",
|
||||||
@ -128,12 +130,18 @@ func (d *LanZou) MakeDir(ctx context.Context, parentDir model.Obj, dirName strin
|
|||||||
"folder_description": "",
|
"folder_description": "",
|
||||||
})
|
})
|
||||||
}, nil)
|
}, nil)
|
||||||
return err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &FileOrFolder{
|
||||||
|
Name: dirName,
|
||||||
|
FolID: utils.Json.Get(data, "text").ToString(),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
return errs.NotImplement
|
return nil, errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *LanZou) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
func (d *LanZou) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
|
||||||
if d.IsCookie() {
|
if d.IsCookie() {
|
||||||
if !srcObj.IsDir() {
|
if !srcObj.IsDir() {
|
||||||
_, err := d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
@ -144,13 +152,16 @@ func (d *LanZou) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
|||||||
"file_id": srcObj.GetID(),
|
"file_id": srcObj.GetID(),
|
||||||
})
|
})
|
||||||
}, nil)
|
}, nil)
|
||||||
return err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return srcObj, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errs.NotImplement
|
return nil, errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *LanZou) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
func (d *LanZou) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
|
||||||
if d.IsCookie() {
|
if d.IsCookie() {
|
||||||
if !srcObj.IsDir() {
|
if !srcObj.IsDir() {
|
||||||
_, err := d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
@ -162,10 +173,14 @@ func (d *LanZou) Rename(ctx context.Context, srcObj model.Obj, newName string) e
|
|||||||
"type": "2",
|
"type": "2",
|
||||||
})
|
})
|
||||||
}, nil)
|
}, nil)
|
||||||
return err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
srcObj.(*FileOrFolder).NameAll = newName
|
||||||
|
return srcObj, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errs.NotImplement
|
return nil, errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *LanZou) Remove(ctx context.Context, obj model.Obj) error {
|
func (d *LanZou) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
@ -189,8 +204,9 @@ func (d *LanZou) Remove(ctx context.Context, obj model.Obj) error {
|
|||||||
return errs.NotImplement
|
return errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *LanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
func (d *LanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
|
||||||
if d.IsCookie() {
|
if d.IsCookie() {
|
||||||
|
var resp RespText[[]FileOrFolder]
|
||||||
_, err := d._post(d.BaseUrl+"/fileup.php", func(req *resty.Request) {
|
_, err := d._post(d.BaseUrl+"/fileup.php", func(req *resty.Request) {
|
||||||
req.SetFormData(map[string]string{
|
req.SetFormData(map[string]string{
|
||||||
"task": "1",
|
"task": "1",
|
||||||
@ -198,8 +214,11 @@ func (d *LanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
|||||||
"name": stream.GetName(),
|
"name": stream.GetName(),
|
||||||
"folder_id": dstDir.GetID(),
|
"folder_id": dstDir.GetID(),
|
||||||
}).SetFileReader("upload_file", stream.GetName(), stream).SetContext(ctx)
|
}).SetFileReader("upload_file", stream.GetName(), stream).SetContext(ctx)
|
||||||
}, nil, true)
|
}, &resp, true)
|
||||||
return err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &resp.Text[0], nil
|
||||||
}
|
}
|
||||||
return errs.NotImplement
|
return nil, errs.NotImplement
|
||||||
}
|
}
|
||||||
|
@ -175,3 +175,18 @@ func formToMap(from string) map[string]string {
|
|||||||
}
|
}
|
||||||
return param
|
return param
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var regExpirationTime = regexp.MustCompile(`e=(\d+)`)
|
||||||
|
|
||||||
|
func GetExpirationTime(url string) (etime time.Duration) {
|
||||||
|
exps := regExpirationTime.FindStringSubmatch(url)
|
||||||
|
if len(exps) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timestamp, err := strconv.ParseInt(exps[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
etime = time.Duration(timestamp-time.Now().Unix()) * time.Second
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -9,8 +9,12 @@ import (
|
|||||||
var ErrFileShareCancel = errors.New("file sharing cancellation")
|
var ErrFileShareCancel = errors.New("file sharing cancellation")
|
||||||
var ErrFileNotExist = errors.New("file does not exist")
|
var ErrFileNotExist = errors.New("file does not exist")
|
||||||
|
|
||||||
type FilesOrFoldersResp struct {
|
type RespText[T any] struct {
|
||||||
Text []FileOrFolder `json:"text"`
|
Text T `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RespInfo[T any] struct {
|
||||||
|
Info T `json:"info"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileOrFolder struct {
|
type FileOrFolder struct {
|
||||||
@ -81,9 +85,6 @@ func (f *FileOrFolder) GetShareInfo() *FileShare {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 通过ID获取文件/文件夹分享信息 */
|
/* 通过ID获取文件/文件夹分享信息 */
|
||||||
type FileShareResp struct {
|
|
||||||
Info FileShare `json:"info"`
|
|
||||||
}
|
|
||||||
type FileShare struct {
|
type FileShare struct {
|
||||||
Pwd string `json:"pwd"`
|
Pwd string `json:"pwd"`
|
||||||
Onof string `json:"onof"`
|
Onof string `json:"onof"`
|
||||||
|
@ -114,7 +114,7 @@ func (d *LanZou) GetAllFiles(folderID string) ([]model.Obj, error) {
|
|||||||
|
|
||||||
// 通过ID获取文件夹
|
// 通过ID获取文件夹
|
||||||
func (d *LanZou) GetFolders(folderID string) ([]FileOrFolder, error) {
|
func (d *LanZou) GetFolders(folderID string) ([]FileOrFolder, error) {
|
||||||
var resp FilesOrFoldersResp
|
var resp RespText[[]FileOrFolder]
|
||||||
_, err := d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
req.SetFormData(map[string]string{
|
req.SetFormData(map[string]string{
|
||||||
"task": "47",
|
"task": "47",
|
||||||
@ -131,7 +131,7 @@ func (d *LanZou) GetFolders(folderID string) ([]FileOrFolder, error) {
|
|||||||
func (d *LanZou) GetFiles(folderID string) ([]FileOrFolder, error) {
|
func (d *LanZou) GetFiles(folderID string) ([]FileOrFolder, error) {
|
||||||
files := make([]FileOrFolder, 0)
|
files := make([]FileOrFolder, 0)
|
||||||
for pg := 1; ; pg++ {
|
for pg := 1; ; pg++ {
|
||||||
var resp FilesOrFoldersResp
|
var resp RespText[[]FileOrFolder]
|
||||||
_, err := d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
req.SetFormData(map[string]string{
|
req.SetFormData(map[string]string{
|
||||||
"task": "5",
|
"task": "5",
|
||||||
@ -152,7 +152,7 @@ func (d *LanZou) GetFiles(folderID string) ([]FileOrFolder, error) {
|
|||||||
|
|
||||||
// 通过ID获取文件夹分享地址
|
// 通过ID获取文件夹分享地址
|
||||||
func (d *LanZou) getFolderShareUrlByID(fileID string) (*FileShare, error) {
|
func (d *LanZou) getFolderShareUrlByID(fileID string) (*FileShare, error) {
|
||||||
var resp FileShareResp
|
var resp RespInfo[FileShare]
|
||||||
_, err := d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
req.SetFormData(map[string]string{
|
req.SetFormData(map[string]string{
|
||||||
"task": "18",
|
"task": "18",
|
||||||
@ -167,7 +167,7 @@ func (d *LanZou) getFolderShareUrlByID(fileID string) (*FileShare, error) {
|
|||||||
|
|
||||||
// 通过ID获取文件分享地址
|
// 通过ID获取文件分享地址
|
||||||
func (d *LanZou) getFileShareUrlByID(fileID string) (*FileShare, error) {
|
func (d *LanZou) getFileShareUrlByID(fileID string) (*FileShare, error) {
|
||||||
var resp FileShareResp
|
var resp RespInfo[FileShare]
|
||||||
_, err := d.doupload(func(req *resty.Request) {
|
_, err := d.doupload(func(req *resty.Request) {
|
||||||
req.SetFormData(map[string]string{
|
req.SetFormData(map[string]string{
|
||||||
"task": "22",
|
"task": "22",
|
||||||
|
@ -38,13 +38,6 @@ func list(ctx context.Context, path string, refresh ...bool) ([]model.Obj, error
|
|||||||
om.InitHideReg(meta.Hide)
|
om.InitHideReg(meta.Hide)
|
||||||
}
|
}
|
||||||
objs := om.Merge(virtualFiles, _objs...)
|
objs := om.Merge(virtualFiles, _objs...)
|
||||||
// sort objs
|
|
||||||
if storage != nil {
|
|
||||||
if storage.Config().LocalSort {
|
|
||||||
model.SortFiles(objs, storage.GetStorage().OrderBy, storage.GetStorage().OrderDirection)
|
|
||||||
}
|
|
||||||
model.ExtractFolder(objs, storage.GetStorage().ExtractFolder)
|
|
||||||
}
|
|
||||||
return objs, nil
|
return objs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/generic_sync"
|
||||||
"github.com/alist-org/alist/v3/pkg/singleflight"
|
"github.com/alist-org/alist/v3/pkg/singleflight"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -49,6 +50,8 @@ func delCacheObj(storage driver.Driver, path string, obj model.Obj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var addSortDebounceMap generic_sync.MapOf[string, func(func())]
|
||||||
|
|
||||||
func addCacheObj(storage driver.Driver, path string, newObj model.Obj) {
|
func addCacheObj(storage driver.Driver, path string, newObj model.Obj) {
|
||||||
key := Key(storage, path)
|
key := Key(storage, path)
|
||||||
objs, ok := listCache.Get(key)
|
objs, ok := listCache.Get(key)
|
||||||
@ -59,7 +62,24 @@ func addCacheObj(storage driver.Driver, path string, newObj model.Obj) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objs = append(objs, newObj)
|
|
||||||
|
// Simple separation of files and folders
|
||||||
|
if len(objs) > 0 && objs[len(objs)-1].IsDir() == newObj.IsDir() {
|
||||||
|
objs = append(objs, newObj)
|
||||||
|
} else {
|
||||||
|
objs = append([]model.Obj{newObj}, objs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if storage.Config().LocalSort {
|
||||||
|
debounce, _ := addSortDebounceMap.LoadOrStore(key, utils.NewDebounce(time.Minute))
|
||||||
|
log.Debug("addCacheObj: wait start sort")
|
||||||
|
debounce(func() {
|
||||||
|
log.Debug("addCacheObj: start sort")
|
||||||
|
model.SortFiles(objs, storage.GetStorage().OrderBy, storage.GetStorage().OrderDirection)
|
||||||
|
addSortDebounceMap.Delete(key)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
|
listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,6 +133,13 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
|
|||||||
hook(args.ReqPath, files)
|
hook(args.ReqPath, files)
|
||||||
}
|
}
|
||||||
}(args.ReqPath, files)
|
}(args.ReqPath, files)
|
||||||
|
|
||||||
|
// sort objs
|
||||||
|
if storage.Config().LocalSort {
|
||||||
|
model.SortFiles(files, storage.GetStorage().OrderBy, storage.GetStorage().OrderDirection)
|
||||||
|
}
|
||||||
|
model.ExtractFolder(files, storage.GetStorage().ExtractFolder)
|
||||||
|
|
||||||
if !storage.Config().NoCache {
|
if !storage.Config().NoCache {
|
||||||
if len(files) > 0 {
|
if len(files) > 0 {
|
||||||
log.Debugf("set cache: %s => %+v", key, files)
|
log.Debugf("set cache: %s => %+v", key, files)
|
||||||
|
@ -1,8 +1,37 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func MustParseCNTime(str string) time.Time {
|
func MustParseCNTime(str string) time.Time {
|
||||||
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05 -07", str+" +08", time.Local)
|
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05 -07", str+" +08", time.Local)
|
||||||
return lastOpTime
|
return lastOpTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewDebounce(interval time.Duration) func(f func()) {
|
||||||
|
var timer *time.Timer
|
||||||
|
var lock sync.Mutex
|
||||||
|
return func(f func()) {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
timer = time.AfterFunc(interval, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDebounce2(interval time.Duration, f func()) func() {
|
||||||
|
var timer *time.Timer
|
||||||
|
var lock sync.Mutex
|
||||||
|
return func() {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
if timer == nil {
|
||||||
|
timer = time.AfterFunc(interval, f)
|
||||||
|
}
|
||||||
|
(*time.Timer)(timer).Reset(interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user