github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/filesystem/filesystem.go (about)

     1  package filesystem
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	model "github.com/cloudreve/Cloudreve/v3/models"
     7  	"github.com/cloudreve/Cloudreve/v3/pkg/cluster"
     8  	"github.com/cloudreve/Cloudreve/v3/pkg/conf"
     9  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver"
    10  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/cos"
    11  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/googledrive"
    12  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/local"
    13  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/onedrive"
    14  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/oss"
    15  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/qiniu"
    16  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/remote"
    17  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/s3"
    18  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/shadow/masterinslave"
    19  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/shadow/slaveinmaster"
    20  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/upyun"
    21  	"github.com/cloudreve/Cloudreve/v3/pkg/request"
    22  	"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
    23  	"github.com/gin-gonic/gin"
    24  	cossdk "github.com/tencentyun/cos-go-sdk-v5"
    25  	"net/http"
    26  	"net/url"
    27  	"sync"
    28  )
    29  
    30  // FSPool 文件系统资源池
    31  var FSPool = sync.Pool{
    32  	New: func() interface{} {
    33  		return &FileSystem{}
    34  	},
    35  }
    36  
    37  // FileSystem 管理文件的文件系统
    38  type FileSystem struct {
    39  	// 文件系统所有者
    40  	User *model.User
    41  	// 操作文件使用的存储策略
    42  	Policy *model.Policy
    43  	// 当前正在处理的文件对象
    44  	FileTarget []model.File
    45  	// 当前正在处理的目录对象
    46  	DirTarget []model.Folder
    47  	// 相对根目录
    48  	Root *model.Folder
    49  	// 互斥锁
    50  	Lock sync.Mutex
    51  
    52  	/*
    53  	   钩子函数
    54  	*/
    55  	Hooks map[string][]Hook
    56  
    57  	/*
    58  	   文件系统处理适配器
    59  	*/
    60  	Handler driver.Handler
    61  
    62  	// 回收锁
    63  	recycleLock sync.Mutex
    64  }
    65  
    66  // getEmptyFS 从pool中获取新的FileSystem
    67  func getEmptyFS() *FileSystem {
    68  	fs := FSPool.Get().(*FileSystem)
    69  	return fs
    70  }
    71  
    72  // Recycle 回收FileSystem资源
    73  func (fs *FileSystem) Recycle() {
    74  	fs.recycleLock.Lock()
    75  	fs.reset()
    76  	FSPool.Put(fs)
    77  }
    78  
    79  // reset 重设文件系统,以便回收使用
    80  func (fs *FileSystem) reset() {
    81  	fs.User = nil
    82  	fs.CleanTargets()
    83  	fs.Policy = nil
    84  	fs.Hooks = nil
    85  	fs.Handler = nil
    86  	fs.Root = nil
    87  	fs.Lock = sync.Mutex{}
    88  	fs.recycleLock = sync.Mutex{}
    89  }
    90  
    91  // NewFileSystem 初始化一个文件系统
    92  func NewFileSystem(user *model.User) (*FileSystem, error) {
    93  	fs := getEmptyFS()
    94  	fs.User = user
    95  	fs.Policy = &fs.User.Policy
    96  
    97  	// 分配存储策略适配器
    98  	err := fs.DispatchHandler()
    99  
   100  	return fs, err
   101  }
   102  
   103  // NewAnonymousFileSystem 初始化匿名文件系统
   104  func NewAnonymousFileSystem() (*FileSystem, error) {
   105  	fs := getEmptyFS()
   106  	fs.User = &model.User{}
   107  
   108  	// 如果是主机模式下,则为匿名文件系统分配游客用户组
   109  	if conf.SystemConfig.Mode == "master" {
   110  		anonymousGroup, err := model.GetGroupByID(3)
   111  		if err != nil {
   112  			return nil, err
   113  		}
   114  		fs.User.Group = anonymousGroup
   115  	} else {
   116  		// 从机模式下,分配本地策略处理器
   117  		fs.Handler = local.Driver{}
   118  	}
   119  
   120  	return fs, nil
   121  }
   122  
   123  // DispatchHandler 根据存储策略分配文件适配器
   124  func (fs *FileSystem) DispatchHandler() error {
   125  	if fs.Policy == nil {
   126  		return errors.New("未设置存储策略")
   127  	}
   128  	policyType := fs.Policy.Type
   129  	currentPolicy := fs.Policy
   130  
   131  	switch policyType {
   132  	case "mock", "anonymous":
   133  		return nil
   134  	case "local":
   135  		fs.Handler = local.Driver{
   136  			Policy: currentPolicy,
   137  		}
   138  		return nil
   139  	case "remote":
   140  		handler, err := remote.NewDriver(currentPolicy)
   141  		if err != nil {
   142  			return err
   143  		}
   144  
   145  		fs.Handler = handler
   146  	case "qiniu":
   147  		fs.Handler = qiniu.NewDriver(currentPolicy)
   148  		return nil
   149  	case "oss":
   150  		handler, err := oss.NewDriver(currentPolicy)
   151  		fs.Handler = handler
   152  		return err
   153  	case "upyun":
   154  		fs.Handler = upyun.Driver{
   155  			Policy: currentPolicy,
   156  		}
   157  		return nil
   158  	case "onedrive":
   159  		var odErr error
   160  		fs.Handler, odErr = onedrive.NewDriver(currentPolicy)
   161  		return odErr
   162  	case "cos":
   163  		u, _ := url.Parse(currentPolicy.Server)
   164  		b := &cossdk.BaseURL{BucketURL: u}
   165  		fs.Handler = cos.Driver{
   166  			Policy: currentPolicy,
   167  			Client: cossdk.NewClient(b, &http.Client{
   168  				Transport: &cossdk.AuthorizationTransport{
   169  					SecretID:  currentPolicy.AccessKey,
   170  					SecretKey: currentPolicy.SecretKey,
   171  				},
   172  			}),
   173  			HTTPClient: request.NewClient(),
   174  		}
   175  		return nil
   176  	case "s3":
   177  		handler, err := s3.NewDriver(currentPolicy)
   178  		fs.Handler = handler
   179  		return err
   180  	case "googledrive":
   181  		handler, err := googledrive.NewDriver(currentPolicy)
   182  		fs.Handler = handler
   183  		return err
   184  	default:
   185  		return ErrUnknownPolicyType
   186  	}
   187  
   188  	return nil
   189  }
   190  
   191  // NewFileSystemFromContext 从gin.Context创建文件系统
   192  func NewFileSystemFromContext(c *gin.Context) (*FileSystem, error) {
   193  	user, exist := c.Get("user")
   194  	if !exist {
   195  		return NewAnonymousFileSystem()
   196  	}
   197  	fs, err := NewFileSystem(user.(*model.User))
   198  	return fs, err
   199  }
   200  
   201  // NewFileSystemFromCallback 从gin.Context创建回调用文件系统
   202  func NewFileSystemFromCallback(c *gin.Context) (*FileSystem, error) {
   203  	fs, err := NewFileSystemFromContext(c)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	// 获取回调会话
   209  	callbackSessionRaw, ok := c.Get(UploadSessionCtx)
   210  	if !ok {
   211  		return nil, errors.New("upload session not exist")
   212  	}
   213  	callbackSession := callbackSessionRaw.(*serializer.UploadSession)
   214  
   215  	// 重新指向上传策略
   216  	fs.Policy = &callbackSession.Policy
   217  	err = fs.DispatchHandler()
   218  
   219  	return fs, err
   220  }
   221  
   222  // SwitchToSlaveHandler 将负责上传的 Handler 切换为从机节点
   223  func (fs *FileSystem) SwitchToSlaveHandler(node cluster.Node) {
   224  	fs.Handler = slaveinmaster.NewDriver(node, fs.Handler, fs.Policy)
   225  }
   226  
   227  // SwitchToShadowHandler 将负责上传的 Handler 切换为从机节点转存使用的影子处理器
   228  func (fs *FileSystem) SwitchToShadowHandler(master cluster.Node, masterURL, masterID string) {
   229  	switch fs.Policy.Type {
   230  	case "local":
   231  		fs.Policy.Type = "remote"
   232  		fs.Policy.Server = masterURL
   233  		fs.Policy.AccessKey = fmt.Sprintf("%d", master.ID())
   234  		fs.Policy.SecretKey = master.DBModel().MasterKey
   235  		fs.DispatchHandler()
   236  	case "onedrive":
   237  		fs.Policy.MasterID = masterID
   238  	}
   239  
   240  	fs.Handler = masterinslave.NewDriver(master, fs.Handler, fs.Policy)
   241  }
   242  
   243  // SetTargetFile 设置当前处理的目标文件
   244  func (fs *FileSystem) SetTargetFile(files *[]model.File) {
   245  	if len(fs.FileTarget) == 0 {
   246  		fs.FileTarget = *files
   247  	} else {
   248  		fs.FileTarget = append(fs.FileTarget, *files...)
   249  	}
   250  
   251  }
   252  
   253  // SetTargetDir 设置当前处理的目标目录
   254  func (fs *FileSystem) SetTargetDir(dirs *[]model.Folder) {
   255  	if len(fs.DirTarget) == 0 {
   256  		fs.DirTarget = *dirs
   257  	} else {
   258  		fs.DirTarget = append(fs.DirTarget, *dirs...)
   259  	}
   260  
   261  }
   262  
   263  // SetTargetFileByIDs 根据文件ID设置目标文件,忽略用户ID
   264  func (fs *FileSystem) SetTargetFileByIDs(ids []uint) error {
   265  	files, err := model.GetFilesByIDs(ids, 0)
   266  	if err != nil || len(files) == 0 {
   267  		return ErrFileExisted.WithError(err)
   268  	}
   269  	fs.SetTargetFile(&files)
   270  	return nil
   271  }
   272  
   273  // SetTargetByInterface 根据 model.File 或者 model.Folder 设置目标对象
   274  // TODO 测试
   275  func (fs *FileSystem) SetTargetByInterface(target interface{}) error {
   276  	if file, ok := target.(*model.File); ok {
   277  		fs.SetTargetFile(&[]model.File{*file})
   278  		return nil
   279  	}
   280  	if folder, ok := target.(*model.Folder); ok {
   281  		fs.SetTargetDir(&[]model.Folder{*folder})
   282  		return nil
   283  	}
   284  
   285  	return ErrObjectNotExist
   286  }
   287  
   288  // CleanTargets 清空目标
   289  func (fs *FileSystem) CleanTargets() {
   290  	fs.FileTarget = fs.FileTarget[:0]
   291  	fs.DirTarget = fs.DirTarget[:0]
   292  }