Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
a8ff74c2
authored
Feb 04, 2026
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(cache): enhance disk cache management with concurrency control and cleanup optimizations
parent
9ef9e788
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
125 additions
and
81 deletions
+125
-81
common/disk_cache.go
+5
-1
common/disk_cache_config.go
+6
-2
controller/performance.go
+9
-16
service/file_service.go
+63
-43
service/token_counter.go
+6
-4
types/file_source.go
+30
-12
web/src/i18n/locales/zh.json
+3
-0
web/src/pages/Setting/Performance/SettingsPerformance.jsx
+3
-3
No files found.
common/disk_cache.go
View file @
a8ff74c2
...
...
@@ -127,7 +127,11 @@ func CleanupOldDiskCacheFiles(maxAge time.Duration) error {
continue
}
if
now
.
Sub
(
info
.
ModTime
())
>
maxAge
{
os
.
Remove
(
filepath
.
Join
(
dir
,
entry
.
Name
()))
// 注意:后台清理任务删除文件时,由于无法得知原始 base64Size,
// 只能按磁盘文件大小扣减。这在目前 base64 存储模式下是准确的。
if
err
:=
os
.
Remove
(
filepath
.
Join
(
dir
,
entry
.
Name
()));
err
==
nil
{
DecrementDiskFiles
(
info
.
Size
())
}
}
}
return
nil
...
...
common/disk_cache_config.go
View file @
a8ff74c2
...
...
@@ -113,8 +113,12 @@ func IncrementDiskFiles(size int64) {
// DecrementDiskFiles 减少磁盘文件计数
func
DecrementDiskFiles
(
size
int64
)
{
atomic
.
AddInt64
(
&
diskCacheStats
.
ActiveDiskFiles
,
-
1
)
atomic
.
AddInt64
(
&
diskCacheStats
.
CurrentDiskUsageBytes
,
-
size
)
if
atomic
.
AddInt64
(
&
diskCacheStats
.
ActiveDiskFiles
,
-
1
)
<
0
{
atomic
.
StoreInt64
(
&
diskCacheStats
.
ActiveDiskFiles
,
0
)
}
if
atomic
.
AddInt64
(
&
diskCacheStats
.
CurrentDiskUsageBytes
,
-
size
)
<
0
{
atomic
.
StoreInt64
(
&
diskCacheStats
.
CurrentDiskUsageBytes
,
0
)
}
}
// IncrementMemoryBuffers 增加内存缓存计数
...
...
controller/performance.go
View file @
a8ff74c2
...
...
@@ -4,6 +4,7 @@ import (
"net/http"
"os"
"runtime"
"time"
"github.com/QuantumNous/new-api/common"
"github.com/gin-gonic/gin"
...
...
@@ -77,10 +78,8 @@ type PerformanceConfig struct {
// GetPerformanceStats 获取性能统计信息
func
GetPerformanceStats
(
c
*
gin
.
Context
)
{
// 先同步磁盘缓存统计,确保显示准确
common
.
SyncDiskCacheStats
()
// 获取缓存统计
// 不再每次获取统计都全量扫描磁盘,依赖原子计数器保证性能
// 仅在系统启动或显式清理时同步
cacheStats
:=
common
.
GetDiskCacheStats
()
// 获取内存统计
...
...
@@ -123,25 +122,19 @@ func GetPerformanceStats(c *gin.Context) {
})
}
// ClearDiskCache 清理磁盘缓存
// ClearDiskCache 清理
不活跃的
磁盘缓存
func
ClearDiskCache
(
c
*
gin
.
Context
)
{
// 使用统一的缓存目录
dir
:=
common
.
GetDiskCacheDir
()
// 删除缓存目录
err
:=
os
.
RemoveAll
(
dir
)
if
err
!=
nil
&&
!
os
.
IsNotExist
(
err
)
{
// 清理超过 10 分钟未使用的缓存文件
// 10 分钟是一个安全的阈值,确保正在进行的请求不会被误删
err
:=
common
.
CleanupOldDiskCacheFiles
(
10
*
time
.
Minute
)
if
err
!=
nil
{
common
.
ApiError
(
c
,
err
)
return
}
// 重置统计(包括命中次数和使用量)
common
.
ResetDiskCacheStats
()
common
.
ResetDiskCacheUsage
()
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"message"
:
"磁盘缓存已清理"
,
"message"
:
"
不活跃的
磁盘缓存已清理"
,
})
}
...
...
service/file_service.go
View file @
a8ff74c2
...
...
@@ -36,11 +36,44 @@ func LoadFileSource(c *gin.Context, source *types.FileSource, reason ...string)
return
nil
,
fmt
.
Errorf
(
"file source is nil"
)
}
// 如果已有缓存,直接返回
if
common
.
DebugEnabled
{
logger
.
LogDebug
(
c
,
fmt
.
Sprintf
(
"LoadFileSource starting for: %s"
,
source
.
GetIdentifier
()))
}
// 1. 快速检查内部缓存
if
source
.
HasCache
()
{
// 即使命中内部缓存,也要确保注册到清理列表(如果尚未注册)
if
c
!=
nil
{
registerSourceForCleanup
(
c
,
source
)
}
return
source
.
GetCache
(),
nil
}
// 2. 加锁保护加载过程
source
.
Mu
()
.
Lock
()
defer
source
.
Mu
()
.
Unlock
()
// 3. 双重检查
if
source
.
HasCache
()
{
if
c
!=
nil
{
registerSourceForCleanup
(
c
,
source
)
}
return
source
.
GetCache
(),
nil
}
// 4. 如果是 URL,检查 Context 缓存
var
contextKey
string
if
source
.
IsURL
()
&&
c
!=
nil
{
contextKey
=
getContextCacheKey
(
source
.
URL
)
if
cachedData
,
exists
:=
c
.
Get
(
contextKey
);
exists
{
data
:=
cachedData
.
(
*
types
.
CachedFileData
)
source
.
SetCache
(
data
)
registerSourceForCleanup
(
c
,
source
)
return
data
,
nil
}
}
// 5. 执行加载逻辑
var
cachedData
*
types
.
CachedFileData
var
err
error
...
...
@@ -54,10 +87,13 @@ func LoadFileSource(c *gin.Context, source *types.FileSource, reason ...string)
return
nil
,
err
}
// 设置缓存
//
6.
设置缓存
source
.
SetCache
(
cachedData
)
if
contextKey
!=
""
&&
c
!=
nil
{
c
.
Set
(
contextKey
,
cachedData
)
}
// 注册到 context 以便请求结束时自动清理
//
7.
注册到 context 以便请求结束时自动清理
if
c
!=
nil
{
registerSourceForCleanup
(
c
,
source
)
}
...
...
@@ -67,6 +103,10 @@ func LoadFileSource(c *gin.Context, source *types.FileSource, reason ...string)
// registerSourceForCleanup 注册 FileSource 到 context 以便请求结束时清理
func
registerSourceForCleanup
(
c
*
gin
.
Context
,
source
*
types
.
FileSource
)
{
if
source
.
IsRegistered
()
{
return
}
key
:=
string
(
constant
.
ContextKeyFileSourcesToCleanup
)
var
sources
[]
*
types
.
FileSource
if
existing
,
exists
:=
c
.
Get
(
key
);
exists
{
...
...
@@ -74,6 +114,7 @@ func registerSourceForCleanup(c *gin.Context, source *types.FileSource) {
}
sources
=
append
(
sources
,
source
)
c
.
Set
(
key
,
sources
)
source
.
SetRegistered
(
true
)
}
// CleanupFileSources 清理请求中所有注册的 FileSource
...
...
@@ -83,9 +124,6 @@ func CleanupFileSources(c *gin.Context) {
if
sources
,
exists
:=
c
.
Get
(
key
);
exists
{
for
_
,
source
:=
range
sources
.
([]
*
types
.
FileSource
)
{
if
cache
:=
source
.
GetCache
();
cache
!=
nil
{
if
cache
.
IsDisk
()
{
common
.
DecrementDiskFiles
(
cache
.
Size
)
}
cache
.
Close
()
}
}
...
...
@@ -94,21 +132,13 @@ func CleanupFileSources(c *gin.Context) {
}
// loadFromURL 从 URL 加载文件
// 支持磁盘缓存:当文件大小超过阈值且磁盘缓存可用时,将数据存储到磁盘
func
loadFromURL
(
c
*
gin
.
Context
,
url
string
,
reason
...
string
)
(
*
types
.
CachedFileData
,
error
)
{
contextKey
:=
getContextCacheKey
(
url
)
// 检查 context 缓存
if
cachedData
,
exists
:=
c
.
Get
(
contextKey
);
exists
{
if
common
.
DebugEnabled
{
logger
.
LogDebug
(
c
,
fmt
.
Sprintf
(
"Using cached file data for URL: %s"
,
url
))
}
return
cachedData
.
(
*
types
.
CachedFileData
),
nil
}
// 下载文件
var
maxFileSize
=
constant
.
MaxFileDownloadMB
*
1024
*
1024
if
common
.
DebugEnabled
{
logger
.
LogDebug
(
c
,
"loadFromURL: initiating download"
)
}
resp
,
err
:=
DoDownloadRequest
(
url
,
reason
...
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to download file from %s: %w"
,
url
,
err
)
...
...
@@ -120,6 +150,9 @@ func loadFromURL(c *gin.Context, url string, reason ...string) (*types.CachedFil
}
// 读取文件内容(限制大小)
if
common
.
DebugEnabled
{
logger
.
LogDebug
(
c
,
"loadFromURL: reading response body"
)
}
fileBytes
,
err
:=
io
.
ReadAll
(
io
.
LimitReader
(
resp
.
Body
,
int64
(
maxFileSize
+
1
)))
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to read file content: %w"
,
err
)
...
...
@@ -147,6 +180,10 @@ func loadFromURL(c *gin.Context, url string, reason ...string) (*types.CachedFil
cachedData
=
types
.
NewMemoryCachedData
(
base64Data
,
mimeType
,
int64
(
len
(
fileBytes
)))
}
else
{
cachedData
=
types
.
NewDiskCachedData
(
diskPath
,
mimeType
,
int64
(
len
(
fileBytes
)))
cachedData
.
DiskSize
=
base64Size
cachedData
.
OnClose
=
func
(
size
int64
)
{
common
.
DecrementDiskFiles
(
size
)
}
common
.
IncrementDiskFiles
(
base64Size
)
if
common
.
DebugEnabled
{
logger
.
LogDebug
(
c
,
fmt
.
Sprintf
(
"File cached to disk: %s, size: %d bytes"
,
diskPath
,
base64Size
))
...
...
@@ -159,6 +196,9 @@ func loadFromURL(c *gin.Context, url string, reason ...string) (*types.CachedFil
// 如果是图片,尝试获取图片配置
if
strings
.
HasPrefix
(
mimeType
,
"image/"
)
{
if
common
.
DebugEnabled
{
logger
.
LogDebug
(
c
,
"loadFromURL: decoding image config"
)
}
config
,
format
,
err
:=
decodeImageConfig
(
fileBytes
)
if
err
==
nil
{
cachedData
.
ImageConfig
=
&
config
...
...
@@ -170,9 +210,6 @@ func loadFromURL(c *gin.Context, url string, reason ...string) (*types.CachedFil
}
}
// 存入 context 缓存
c
.
Set
(
contextKey
,
cachedData
)
return
cachedData
,
nil
}
...
...
@@ -187,7 +224,6 @@ func writeToDiskCache(base64Data string) (string, error) {
}
// smartDetectMimeType 智能检测 MIME 类型
// 优先级:Content-Type header > Content-Disposition filename > URL 路径 > 内容嗅探 > 图片解码
func
smartDetectMimeType
(
resp
*
http
.
Response
,
url
string
,
fileBytes
[]
byte
)
string
{
// 1. 尝试从 Content-Type header 获取
mimeType
:=
resp
.
Header
.
Get
(
"Content-Type"
)
...
...
@@ -259,13 +295,11 @@ func loadFromBase64(base64String string, providedMimeType string) (*types.Cached
// 处理 data: 前缀
if
strings
.
HasPrefix
(
base64String
,
"data:"
)
{
// 格式: data:mime/type;base64,xxxxx
idx
:=
strings
.
Index
(
base64String
,
","
)
if
idx
!=
-
1
{
header
:=
base64String
[
:
idx
]
cleanBase64
=
base64String
[
idx
+
1
:
]
// 从 header 提取 MIME 类型
if
strings
.
Contains
(
header
,
":"
)
&&
strings
.
Contains
(
header
,
";"
)
{
mimeStart
:=
strings
.
Index
(
header
,
":"
)
+
1
mimeEnd
:=
strings
.
Index
(
header
,
";"
)
...
...
@@ -280,36 +314,34 @@ func loadFromBase64(base64String string, providedMimeType string) (*types.Cached
cleanBase64
=
base64String
}
// 使用提供的 MIME 类型(如果有)
if
providedMimeType
!=
""
{
mimeType
=
providedMimeType
}
// 解码 base64
decodedData
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
cleanBase64
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to decode base64 data: %w"
,
err
)
}
// 判断是否使用磁盘缓存(对于 base64 内联数据也支持磁盘缓存)
base64Size
:=
int64
(
len
(
cleanBase64
))
var
cachedData
*
types
.
CachedFileData
if
shouldUseDiskCache
(
base64Size
)
{
// 使用磁盘缓存
diskPath
,
err
:=
writeToDiskCache
(
cleanBase64
)
if
err
!=
nil
{
// 磁盘缓存失败,回退到内存
cachedData
=
types
.
NewMemoryCachedData
(
cleanBase64
,
mimeType
,
int64
(
len
(
decodedData
)))
}
else
{
cachedData
=
types
.
NewDiskCachedData
(
diskPath
,
mimeType
,
int64
(
len
(
decodedData
)))
cachedData
.
DiskSize
=
base64Size
cachedData
.
OnClose
=
func
(
size
int64
)
{
common
.
DecrementDiskFiles
(
size
)
}
common
.
IncrementDiskFiles
(
base64Size
)
}
}
else
{
cachedData
=
types
.
NewMemoryCachedData
(
cleanBase64
,
mimeType
,
int64
(
len
(
decodedData
)))
}
// 如果是图片或 MIME 类型未知,尝试解码图片获取更多信息
if
mimeType
==
""
||
strings
.
HasPrefix
(
mimeType
,
"image/"
)
{
config
,
format
,
err
:=
decodeImageConfig
(
decodedData
)
if
err
==
nil
{
...
...
@@ -324,8 +356,7 @@ func loadFromBase64(base64String string, providedMimeType string) (*types.Cached
return
cachedData
,
nil
}
// GetImageConfig 获取图片配置(宽高等信息)
// 会自动处理缓存,避免重复下载/解码
// GetImageConfig 获取图片配置
func
GetImageConfig
(
c
*
gin
.
Context
,
source
*
types
.
FileSource
)
(
image
.
Config
,
string
,
error
)
{
cachedData
,
err
:=
LoadFileSource
(
c
,
source
,
"get_image_config"
)
if
err
!=
nil
{
...
...
@@ -336,7 +367,6 @@ func GetImageConfig(c *gin.Context, source *types.FileSource) (image.Config, str
return
*
cachedData
.
ImageConfig
,
cachedData
.
ImageFormat
,
nil
}
// 如果缓存中没有图片配置,尝试解码
base64Str
,
err
:=
cachedData
.
GetBase64Data
()
if
err
!=
nil
{
return
image
.
Config
{},
""
,
fmt
.
Errorf
(
"failed to get base64 data: %w"
,
err
)
...
...
@@ -351,7 +381,6 @@ func GetImageConfig(c *gin.Context, source *types.FileSource) (image.Config, str
return
image
.
Config
{},
""
,
err
}
// 更新缓存
cachedData
.
ImageConfig
=
&
config
cachedData
.
ImageFormat
=
format
...
...
@@ -359,8 +388,6 @@ func GetImageConfig(c *gin.Context, source *types.FileSource) (image.Config, str
}
// GetBase64Data 获取 base64 编码的数据
// 会自动处理缓存,避免重复下载
// 支持内存缓存和磁盘缓存
func
GetBase64Data
(
c
*
gin
.
Context
,
source
*
types
.
FileSource
,
reason
...
string
)
(
string
,
string
,
error
)
{
cachedData
,
err
:=
LoadFileSource
(
c
,
source
,
reason
...
)
if
err
!=
nil
{
...
...
@@ -375,12 +402,10 @@ func GetBase64Data(c *gin.Context, source *types.FileSource, reason ...string) (
// GetMimeType 获取文件的 MIME 类型
func
GetMimeType
(
c
*
gin
.
Context
,
source
*
types
.
FileSource
)
(
string
,
error
)
{
// 如果已经有缓存,直接返回
if
source
.
HasCache
()
{
return
source
.
GetCache
()
.
MimeType
,
nil
}
// 如果是 URL,尝试只获取 header 而不下载完整文件
if
source
.
IsURL
()
{
mimeType
,
err
:=
GetFileTypeFromUrl
(
c
,
source
.
URL
,
"get_mime_type"
)
if
err
==
nil
&&
mimeType
!=
""
&&
mimeType
!=
"application/octet-stream"
{
...
...
@@ -388,7 +413,6 @@ func GetMimeType(c *gin.Context, source *types.FileSource) (string, error) {
}
}
// 否则加载完整数据
cachedData
,
err
:=
LoadFileSource
(
c
,
source
,
"get_mime_type"
)
if
err
!=
nil
{
return
""
,
err
...
...
@@ -396,7 +420,7 @@ func GetMimeType(c *gin.Context, source *types.FileSource) (string, error) {
return
cachedData
.
MimeType
,
nil
}
// DetectFileType 检测文件类型
(image/audio/video/file)
// DetectFileType 检测文件类型
func
DetectFileType
(
mimeType
string
)
types
.
FileType
{
if
strings
.
HasPrefix
(
mimeType
,
"image/"
)
{
return
types
.
FileTypeImage
...
...
@@ -414,13 +438,11 @@ func DetectFileType(mimeType string) types.FileType {
func
decodeImageConfig
(
data
[]
byte
)
(
image
.
Config
,
string
,
error
)
{
reader
:=
bytes
.
NewReader
(
data
)
// 尝试标准格式
config
,
format
,
err
:=
image
.
DecodeConfig
(
reader
)
if
err
==
nil
{
return
config
,
format
,
nil
}
// 尝试 webp
reader
.
Seek
(
0
,
io
.
SeekStart
)
config
,
err
=
webp
.
DecodeConfig
(
reader
)
if
err
==
nil
{
...
...
@@ -432,13 +454,11 @@ func decodeImageConfig(data []byte) (image.Config, string, error) {
// guessMimeTypeFromURL 从 URL 猜测 MIME 类型
func
guessMimeTypeFromURL
(
url
string
)
string
{
// 移除查询参数
cleanedURL
:=
url
if
q
:=
strings
.
Index
(
cleanedURL
,
"?"
);
q
!=
-
1
{
cleanedURL
=
cleanedURL
[
:
q
]
}
// 获取最后一段
if
slash
:=
strings
.
LastIndex
(
cleanedURL
,
"/"
);
slash
!=
-
1
&&
slash
+
1
<
len
(
cleanedURL
)
{
last
:=
cleanedURL
[
slash
+
1
:
]
if
dot
:=
strings
.
LastIndex
(
last
,
"."
);
dot
!=
-
1
&&
dot
+
1
<
len
(
last
)
{
...
...
service/token_counter.go
View file @
a8ff74c2
...
...
@@ -258,16 +258,18 @@ func EstimateRequestToken(c *gin.Context, meta *types.TokenCountMeta, info *rela
// 如果文件类型未知且需要获取,通过 MIME 类型检测
if
file
.
FileType
==
""
||
(
file
.
Source
.
IsURL
()
&&
shouldFetchFiles
)
{
mimeType
,
err
:=
GetMimeType
(
c
,
file
.
Source
)
// 注意:这里我们直接调用 LoadFileSource 而不是 GetMimeType
// 因为 GetMimeType 内部可能会调用 GetFileTypeFromUrl (HEAD 请求)
// 而我们这里既然要计算 token,通常需要完整数据
cachedData
,
err
:=
LoadFileSource
(
c
,
file
.
Source
,
"token_counter"
)
if
err
!=
nil
{
if
shouldFetchFiles
{
return
0
,
fmt
.
Errorf
(
"error getting file type: %v"
,
err
)
}
// 如果不需要获取,使用默认类型
continue
}
file
.
MimeType
=
m
imeType
file
.
FileType
=
DetectFileType
(
m
imeType
)
file
.
MimeType
=
cachedData
.
M
imeType
file
.
FileType
=
DetectFileType
(
cachedData
.
M
imeType
)
}
}
...
...
types/file_source.go
View file @
a8ff74c2
...
...
@@ -25,8 +25,14 @@ type FileSource struct {
// 内部缓存(不导出,不序列化)
cachedData
*
CachedFileData
cacheMu
sync
.
RWMutex
cacheLoaded
bool
registered
bool
// 是否已注册到清理列表
mu
sync
.
Mutex
// 保护加载过程
}
// Mu 获取内部锁
func
(
f
*
FileSource
)
Mu
()
*
sync
.
Mutex
{
return
&
f
.
mu
}
// CachedFileData 缓存的文件数据
...
...
@@ -35,14 +41,19 @@ type CachedFileData struct {
base64Data
string
// 内存中的 base64 数据(小文件)
MimeType
string
// MIME 类型
Size
int64
// 文件大小(字节)
DiskSize
int64
// 磁盘缓存实际占用大小(字节,通常是 base64 长度)
ImageConfig
*
image
.
Config
// 图片配置(如果是图片)
ImageFormat
string
// 图片格式(如果是图片)
// 磁盘缓存相关
diskPath
string
// 磁盘缓存文件路径(大文件)
isDisk
bool
// 是否使用磁盘缓存
diskMu
sync
.
Mutex
// 磁盘操作锁
diskMu
sync
.
Mutex
// 磁盘操作锁(保护磁盘文件的读取和删除)
diskClosed
bool
// 是否已关闭/清理
statDecremented
bool
// 是否已扣减统计
// 统计回调,避免循环依赖
OnClose
func
(
size
int64
)
}
// NewMemoryCachedData 创建内存缓存的数据
...
...
@@ -114,7 +125,13 @@ func (c *CachedFileData) Close() error {
c
.
diskClosed
=
true
if
c
.
diskPath
!=
""
{
return
os
.
Remove
(
c
.
diskPath
)
err
:=
os
.
Remove
(
c
.
diskPath
)
// 只有在删除成功且未扣减过统计时,才执行回调
if
err
==
nil
&&
!
c
.
statDecremented
&&
c
.
OnClose
!=
nil
{
c
.
OnClose
(
c
.
DiskSize
)
c
.
statDecremented
=
true
}
return
err
}
return
nil
}
...
...
@@ -170,31 +187,32 @@ func (f *FileSource) GetRawData() string {
// SetCache 设置缓存数据
func
(
f
*
FileSource
)
SetCache
(
data
*
CachedFileData
)
{
f
.
cacheMu
.
Lock
()
defer
f
.
cacheMu
.
Unlock
()
f
.
cachedData
=
data
f
.
cacheLoaded
=
true
}
// IsRegistered 是否已注册到清理列表
func
(
f
*
FileSource
)
IsRegistered
()
bool
{
return
f
.
registered
}
// SetRegistered 设置注册状态
func
(
f
*
FileSource
)
SetRegistered
(
registered
bool
)
{
f
.
registered
=
registered
}
// GetCache 获取缓存数据
func
(
f
*
FileSource
)
GetCache
()
*
CachedFileData
{
f
.
cacheMu
.
RLock
()
defer
f
.
cacheMu
.
RUnlock
()
return
f
.
cachedData
}
// HasCache 是否有缓存
func
(
f
*
FileSource
)
HasCache
()
bool
{
f
.
cacheMu
.
RLock
()
defer
f
.
cacheMu
.
RUnlock
()
return
f
.
cacheLoaded
&&
f
.
cachedData
!=
nil
}
// ClearCache 清除缓存,释放内存和磁盘文件
func
(
f
*
FileSource
)
ClearCache
()
{
f
.
cacheMu
.
Lock
()
defer
f
.
cacheMu
.
Unlock
()
// 如果有缓存数据,先关闭它(会清理磁盘文件)
if
f
.
cachedData
!=
nil
{
f
.
cachedData
.
Close
()
...
...
web/src/i18n/locales/zh.json
View file @
a8ff74c2
...
...
@@ -442,6 +442,9 @@
"兑换人ID"
:
"兑换人ID"
,
"兑换成功!"
:
"兑换成功!"
,
"兑换码充值"
:
"兑换码充值"
,
"确认清理不活跃的磁盘缓存?"
:
"确认清理不活跃的磁盘缓存?"
,
"这将删除超过 10 分钟未使用的临时缓存文件"
:
"这将删除超过 10 分钟未使用的临时缓存文件"
,
"清理不活跃缓存"
:
"清理不活跃缓存"
,
"兑换码创建成功"
:
"兑换码创建成功"
,
"兑换码创建成功,是否下载兑换码?"
:
"兑换码创建成功,是否下载兑换码?"
,
"兑换码创建成功!"
:
"兑换码创建成功!"
,
...
...
web/src/pages/Setting/Performance/SettingsPerformance.jsx
View file @
a8ff74c2
...
...
@@ -291,11 +291,11 @@ export default function SettingsPerformance(props) {
<
div
style=
{
{
display
:
'flex'
,
gap
:
8
,
flexWrap
:
'wrap'
}
}
>
<
Button
onClick=
{
fetchStats
}
>
{
t
(
'刷新统计'
)
}
</
Button
>
<
Popconfirm
title=
{
t
(
'确认清理磁盘缓存?'
)
}
content=
{
t
(
'这将删除
所有
临时缓存文件'
)
}
title=
{
t
(
'确认清理
不活跃的
磁盘缓存?'
)
}
content=
{
t
(
'这将删除
超过 10 分钟未使用的
临时缓存文件'
)
}
onConfirm=
{
clearDiskCache
}
>
<
Button
type=
'warning'
>
{
t
(
'清理
磁盘
缓存'
)
}
</
Button
>
<
Button
type=
'warning'
>
{
t
(
'清理
不活跃
缓存'
)
}
</
Button
>
</
Popconfirm
>
<
Button
onClick=
{
resetStats
}
>
{
t
(
'重置统计'
)
}
</
Button
>
<
Button
onClick=
{
forceGC
}
>
{
t
(
'执行 GC'
)
}
</
Button
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment