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

     1  package task
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"path"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	model "github.com/cloudreve/Cloudreve/v3/models"
    12  	"github.com/cloudreve/Cloudreve/v3/pkg/cluster"
    13  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem"
    14  	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
    15  	"github.com/cloudreve/Cloudreve/v3/pkg/util"
    16  )
    17  
    18  // TransferTask 文件中转任务
    19  type TransferTask struct {
    20  	User      *model.User
    21  	TaskModel *model.Task
    22  	TaskProps TransferProps
    23  	Err       *JobError
    24  
    25  	zipPath string
    26  }
    27  
    28  // TransferProps 中转任务属性
    29  type TransferProps struct {
    30  	Src      []string          `json:"src"`      // 原始文件
    31  	SrcSizes map[string]uint64 `json:"src_size"` // 原始文件的大小信息,从机转存时使用
    32  	Parent   string            `json:"parent"`   // 父目录
    33  	Dst      string            `json:"dst"`      // 目的目录ID
    34  	// 将会保留原始文件的目录结构,Src 除去 Parent 开头作为最终路径
    35  	TrimPath bool `json:"trim_path"`
    36  	// 负责处理中专任务的节点ID
    37  	NodeID uint `json:"node_id"`
    38  }
    39  
    40  // Props 获取任务属性
    41  func (job *TransferTask) Props() string {
    42  	res, _ := json.Marshal(job.TaskProps)
    43  	return string(res)
    44  }
    45  
    46  // Type 获取任务状态
    47  func (job *TransferTask) Type() int {
    48  	return TransferTaskType
    49  }
    50  
    51  // Creator 获取创建者ID
    52  func (job *TransferTask) Creator() uint {
    53  	return job.User.ID
    54  }
    55  
    56  // Model 获取任务的数据库模型
    57  func (job *TransferTask) Model() *model.Task {
    58  	return job.TaskModel
    59  }
    60  
    61  // SetStatus 设定状态
    62  func (job *TransferTask) SetStatus(status int) {
    63  	job.TaskModel.SetStatus(status)
    64  }
    65  
    66  // SetError 设定任务失败信息
    67  func (job *TransferTask) SetError(err *JobError) {
    68  	job.Err = err
    69  	res, _ := json.Marshal(job.Err)
    70  	job.TaskModel.SetError(string(res))
    71  
    72  }
    73  
    74  // SetErrorMsg 设定任务失败信息
    75  func (job *TransferTask) SetErrorMsg(msg string, err error) {
    76  	jobErr := &JobError{Msg: msg}
    77  	if err != nil {
    78  		jobErr.Error = err.Error()
    79  	}
    80  	job.SetError(jobErr)
    81  }
    82  
    83  // GetError 返回任务失败信息
    84  func (job *TransferTask) GetError() *JobError {
    85  	return job.Err
    86  }
    87  
    88  // Do 开始执行任务
    89  func (job *TransferTask) Do() {
    90  	// 创建文件系统
    91  	fs, err := filesystem.NewFileSystem(job.User)
    92  	if err != nil {
    93  		job.SetErrorMsg(err.Error(), nil)
    94  		return
    95  	}
    96  
    97  	successCount := 0
    98  	errorList := make([]string, 0, len(job.TaskProps.Src))
    99  	for _, file := range job.TaskProps.Src {
   100  		dst := path.Join(job.TaskProps.Dst, filepath.Base(file))
   101  		if job.TaskProps.TrimPath {
   102  			// 保留原始目录
   103  			trim := util.FormSlash(job.TaskProps.Parent)
   104  			src := util.FormSlash(file)
   105  			dst = path.Join(job.TaskProps.Dst, strings.TrimPrefix(src, trim))
   106  		}
   107  
   108  		if job.TaskProps.NodeID > 1 {
   109  			// 指定为从机中转
   110  
   111  			// 获取从机节点
   112  			node := cluster.Default.GetNodeByID(job.TaskProps.NodeID)
   113  			if node == nil {
   114  				job.SetErrorMsg("Invalid slave node.", nil)
   115  			}
   116  
   117  			// 切换为从机节点处理上传
   118  			fs.SwitchToSlaveHandler(node)
   119  			err = fs.UploadFromStream(context.Background(), &fsctx.FileStream{
   120  				File:        nil,
   121  				Size:        job.TaskProps.SrcSizes[file],
   122  				Name:        path.Base(dst),
   123  				VirtualPath: path.Dir(dst),
   124  				Src:         file,
   125  			}, false)
   126  		} else {
   127  			// 主机节点中转
   128  			err = fs.UploadFromPath(context.Background(), file, dst, 0)
   129  		}
   130  
   131  		if err != nil {
   132  			errorList = append(errorList, err.Error())
   133  		} else {
   134  			successCount++
   135  			job.TaskModel.SetProgress(successCount)
   136  		}
   137  	}
   138  
   139  	if len(errorList) > 0 {
   140  		job.SetErrorMsg("Failed to transfer one or more file(s).", fmt.Errorf(strings.Join(errorList, "\n")))
   141  	}
   142  
   143  }
   144  
   145  // NewTransferTask 新建中转任务
   146  func NewTransferTask(user uint, src []string, dst, parent string, trim bool, node uint, sizes map[string]uint64) (Job, error) {
   147  	creator, err := model.GetActiveUserByID(user)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	newTask := &TransferTask{
   153  		User: &creator,
   154  		TaskProps: TransferProps{
   155  			Src:      src,
   156  			Parent:   parent,
   157  			Dst:      dst,
   158  			TrimPath: trim,
   159  			NodeID:   node,
   160  			SrcSizes: sizes,
   161  		},
   162  	}
   163  
   164  	record, err := Record(newTask)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	newTask.TaskModel = record
   169  
   170  	return newTask, nil
   171  }
   172  
   173  // NewTransferTaskFromModel 从数据库记录中恢复中转任务
   174  func NewTransferTaskFromModel(task *model.Task) (Job, error) {
   175  	user, err := model.GetActiveUserByID(task.UserID)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	newTask := &TransferTask{
   180  		User:      &user,
   181  		TaskModel: task,
   182  	}
   183  
   184  	err = json.Unmarshal([]byte(task.Props), &newTask.TaskProps)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	return newTask, nil
   190  }