gf/g/os/gspath/gspath.go

228 lines
7.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gspath implements file index and search for folders.
//
// 搜索目录管理,
// 可以添加搜索目录,按照添加的优先级进行文件检索,并在内部进行高效缓存处理(可选)。
// 注意:当开启缓存功能后,在新增/删除文件时,会存在检索延迟。
package gspath
import (
"errors"
"fmt"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/text/gstr"
"os"
"sort"
"strings"
)
// 文件目录搜索管理对象
type SPath struct {
paths *garray.StringArray // 搜索路径,按照优先级进行排序
cache *gmap.StrStrMap // 搜索结果缓存map(如果未nil表示未启用缓存功能)
}
// 文件搜索缓存项
type SPathCacheItem struct {
path string // 文件/目录绝对路径
isDir bool // 是否目录
}
var (
// 单个目录路径对应的SPath对象指针用于路径检索对象复用
pathsMap = gmap.NewStrAnyMap()
pathsCacheMap = gmap.NewStrAnyMap()
)
// 创建一个搜索对象
func New(path string, cache bool) *SPath {
sp := &SPath {
paths : garray.NewStringArray(),
}
if cache {
sp.cache = gmap.NewStrStrMap()
}
if len(path) > 0 {
if _, err := sp.Add(path); err != nil {
//fmt.Errorf(err.Error())
}
}
return sp
}
// 创建/获取一个单例的搜索对象, root必须为目录的绝对路径
func Get(root string, cache bool) *SPath {
return pathsMap.GetOrSetFuncLock(root, func() interface{} {
return New(root, cache)
}).(*SPath)
}
// 检索root目录(必须为绝对路径)下面的name文件的绝对路径indexFiles用于指定当检索到的结果为目录时同时检索是否存在这些indexFiles文件
func Search(root string, name string, indexFiles...string) (filePath string, isDir bool) {
return Get(root, false).Search(name, indexFiles...)
}
// 检索root目录(必须为绝对路径)下面的name文件的绝对路径indexFiles用于指定当检索到的结果为目录时同时检索是否存在这些indexFiles文件
func SearchWithCache(root string, name string, indexFiles...string) (filePath string, isDir bool) {
return Get(root, true).Search(name, indexFiles...)
}
// 设置搜索路径,只保留当前设置项,其他搜索路径被清空
func (sp *SPath) Set(path string) (realPath string, err error) {
realPath = gfile.RealPath(path)
if realPath == "" {
realPath, _ = sp.Search(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + path)
}
}
if realPath == "" {
return realPath, errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
}
// 设置的搜索路径必须为目录
if gfile.IsDir(realPath) {
realPath = strings.TrimRight(realPath, gfile.Separator)
if sp.paths.Search(realPath) != -1 {
for _, v := range sp.paths.Slice() {
sp.removeMonitorByPath(v)
}
}
sp.paths.Clear()
if sp.cache != nil {
sp.cache.Clear()
}
sp.paths.Append(realPath)
sp.updateCacheByPath(realPath)
sp.addMonitorByPath(realPath)
return realPath, nil
} else {
return "", errors.New(path + " should be a folder")
}
}
// 添加搜索路径
func (sp *SPath) Add(path string) (realPath string, err error) {
realPath = gfile.RealPath(path)
if realPath == "" {
realPath, _ = sp.Search(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + path)
}
}
if realPath == "" {
return realPath, errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
}
// 添加的搜索路径必须为目录
if gfile.IsDir(realPath) {
//fmt.Println("gspath:", realPath, sp.paths.Search(realPath))
// 如果已经添加则不再添加
if sp.paths.Search(realPath) < 0 {
realPath = strings.TrimRight(realPath, gfile.Separator)
sp.paths.Append(realPath)
sp.updateCacheByPath(realPath)
sp.addMonitorByPath(realPath)
}
return realPath, nil
} else {
return "", errors.New(path + " should be a folder")
}
}
// 给定的name只是相对文件路径找不到该文件时返回空字符串;
// 当给定indexFiles时如果name是一个目录那么会进一步检索其下对应的indexFiles文件是否存在存在则返回indexFile绝对路径
// 否则返回name目录绝对路径。
func (sp *SPath) Search(name string, indexFiles...string) (filePath string, isDir bool) {
// 不使用缓存
if sp.cache == nil {
sp.paths.LockFunc(func(array []string) {
path := ""
for _, v := range array {
path = v + gfile.Separator + name
if stat, err := os.Stat(path); !os.IsNotExist(err) {
filePath = path
isDir = stat.IsDir()
break
}
}
})
if len(indexFiles) > 0 && isDir {
if name == "/" {
name = ""
}
path := ""
for _, file := range indexFiles {
path = filePath + gfile.Separator + file
if gfile.Exists(path) {
filePath = path
isDir = false
break
}
}
}
return
}
// 使用缓存功能
name = sp.formatCacheName(name)
if v := sp.cache.Get(name); v != "" {
filePath, isDir = sp.parseCacheValue(v)
if len(indexFiles) > 0 && isDir {
if name == "/" {
name = ""
}
for _, file := range indexFiles {
if v := sp.cache.Get(name + "/" + file); v != "" {
return sp.parseCacheValue(v)
}
}
}
}
return
}
// 从搜索路径中移除指定的文件,这样该文件无法给搜索。
// path可以是绝对路径也可以相对路径。
func (sp *SPath) Remove(path string) {
if sp.cache == nil {
return
}
if gfile.Exists(path) {
for _, v := range sp.paths.Slice() {
name := gstr.Replace(path, v, "")
name = sp.formatCacheName(name)
sp.cache.Remove(name)
}
} else {
name := sp.formatCacheName(path)
sp.cache.Remove(name)
}
}
// 返回当前对象搜索目录路径列表
func (sp *SPath) Paths() []string {
return sp.paths.Slice()
}
// 返回当前对象缓存的所有路径列表
func (sp *SPath) AllPaths() []string {
if sp.cache == nil {
return nil
}
paths := sp.cache.Keys()
if len(paths) > 0 {
sort.Strings(paths)
}
return paths
}
// 当前的搜索路径数量
func (sp *SPath) Size() int {
return sp.paths.Len()
}