From 2567526672b50c5cc1e3c2549ada06617c3c3a95 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 28 Oct 2025 11:01:12 +0800 Subject: [PATCH] :art: Improve detection of duplicate insertion of assets https://github.com/siyuan-note/siyuan/issues/16220 Signed-off-by: Daniel <845765@qq.com> --- kernel/cache/asset.go | 50 ++++++++++++++++++++++++++++++++++++++++-- kernel/model/assets.go | 15 +++++++++++++ kernel/model/upload.go | 19 ++++++++++------ 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/kernel/cache/asset.go b/kernel/cache/asset.go index 63cb231bc..fa623de9a 100644 --- a/kernel/cache/asset.go +++ b/kernel/cache/asset.go @@ -28,14 +28,60 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +type AssetHash struct { + Hash string `json:"hash"` + Path string `json:"path"` +} + +var ( + assetHashCache = map[string]*AssetHash{} + assetHashLock = sync.Mutex{} +) + +func RemoveAssetHash(hash string) { + assetHashLock.Lock() + defer assetHashLock.Unlock() + + delete(assetHashCache, hash) +} + +func SetAssetHash(hash, path string) { + assetHashLock.Lock() + defer assetHashLock.Unlock() + + assetHashCache[hash] = &AssetHash{ + Hash: hash, + Path: path, + } +} + +func GetAssetHash(hash string) *AssetHash { + assetHashLock.Lock() + defer assetHashLock.Unlock() + + for _, a := range assetHashCache { + if a.Hash == hash { + if filelock.IsExist(filepath.Join(util.DataDir, a.Path)) { + return a + } + + delete(assetHashCache, hash) + return nil + } + } + return nil +} + type Asset struct { HName string `json:"hName"` Path string `json:"path"` Updated int64 `json:"updated"` } -var assetsCache = map[string]*Asset{} -var assetsLock = sync.Mutex{} +var ( + assetsCache = map[string]*Asset{} + assetsLock = sync.Mutex{} +) func GetAssets() (ret map[string]*Asset) { assetsLock.Lock() diff --git a/kernel/model/assets.go b/kernel/model/assets.go index 3e55a3a09..cdc5a951f 100644 --- a/kernel/model/assets.go +++ b/kernel/model/assets.go @@ -51,6 +51,19 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func GetAssetPathByHash(hash string) string { + assetHash := cache.GetAssetHash(hash) + if nil == assetHash { + sqlAsset := sql.QueryAssetByHash(hash) + if nil == sqlAsset { + return "" + } + cache.SetAssetHash(sqlAsset.Hash, sqlAsset.Path) + return sqlAsset.Path + } + return assetHash.Path +} + func HandleAssetsRemoveEvent(assetAbsPath string) { removeIndexAssetContent(assetAbsPath) removeAssetThumbnail(assetAbsPath) @@ -648,6 +661,7 @@ func RemoveUnusedAssets() (ret []string) { hash, _ := util.GetEtag(p) hashes = append(hashes, hash) + cache.RemoveAssetHash(hash) } } @@ -703,6 +717,7 @@ func RemoveUnusedAsset(p string) (ret string) { hash, _ := util.GetEtag(absPath) sql.BatchRemoveAssetsQueue([]string{hash}) + cache.RemoveAssetHash(hash) } if err = filelock.Remove(absPath); err != nil { diff --git a/kernel/model/upload.go b/kernel/model/upload.go index 53b1f3cb9..49a896a16 100644 --- a/kernel/model/upload.go +++ b/kernel/model/upload.go @@ -29,7 +29,7 @@ import ( "github.com/gin-gonic/gin" "github.com/siyuan-note/filelock" "github.com/siyuan-note/logging" - "github.com/siyuan-note/siyuan/kernel/sql" + "github.com/siyuan-note/siyuan/kernel/cache" "github.com/siyuan-note/siyuan/kernel/treenode" "github.com/siyuan-note/siyuan/kernel/util" ) @@ -90,9 +90,9 @@ func InsertLocalAssets(id string, assetAbsPaths []string, isUpload bool) (succMa return } - if existAsset := sql.QueryAssetByHash(hash); nil != existAsset { + if existAssetPath := GetAssetPathByHash(hash); "" != existAssetPath { // 已经存在同样数据的资源文件的话不重复保存 - succMap[baseName] = existAsset.Path + succMap[baseName] = existAssetPath } else { fName = util.AssetName(fName, ast.NewNodeID()) writePath := filepath.Join(assetsDirPath, fName) @@ -105,7 +105,10 @@ func InsertLocalAssets(id string, assetAbsPaths []string, isUpload bool) (succMa return } f.Close() - succMap[baseName] = "assets/" + fName + + p := "assets/" + fName + succMap[baseName] = p + cache.SetAssetHash(hash, p) } } IncSync() @@ -197,9 +200,9 @@ func Upload(c *gin.Context) { break } - if existAsset := sql.QueryAssetByHash(hash); nil != existAsset { + if existAssetPath := GetAssetPathByHash(hash); "" != existAssetPath { // 已经存在同样数据的资源文件的话不重复保存 - succMap[baseName] = existAsset.Path + succMap[baseName] = existAssetPath } else { if skipIfDuplicated { // 复制 PDF 矩形注解时不再重复插入图片 No longer upload image repeatedly when copying PDF rectangle annotation https://github.com/siyuan-note/siyuan/issues/10666 @@ -310,7 +313,9 @@ func Upload(c *gin.Context) { os.RemoveAll(tmpDir2) } - succMap[baseName] = strings.TrimPrefix(path.Join(relAssetsDirPath, fName), "/") + p := strings.TrimPrefix(path.Join(relAssetsDirPath, fName), "/") + succMap[baseName] = p + cache.SetAssetHash(hash, p) } }