Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/internal/fs/copy.go
1560 views
1
package fs
2
3
import (
4
"context"
5
"fmt"
6
"github.com/alist-org/alist/v3/internal/errs"
7
"net/http"
8
stdpath "path"
9
"time"
10
11
"github.com/alist-org/alist/v3/internal/conf"
12
"github.com/alist-org/alist/v3/internal/driver"
13
"github.com/alist-org/alist/v3/internal/model"
14
"github.com/alist-org/alist/v3/internal/op"
15
"github.com/alist-org/alist/v3/internal/stream"
16
"github.com/alist-org/alist/v3/internal/task"
17
"github.com/alist-org/alist/v3/pkg/utils"
18
"github.com/pkg/errors"
19
"github.com/xhofe/tache"
20
)
21
22
type CopyTask struct {
23
task.TaskExtension
24
Status string `json:"-"` //don't save status to save space
25
SrcObjPath string `json:"src_path"`
26
DstDirPath string `json:"dst_path"`
27
srcStorage driver.Driver `json:"-"`
28
dstStorage driver.Driver `json:"-"`
29
SrcStorageMp string `json:"src_storage_mp"`
30
DstStorageMp string `json:"dst_storage_mp"`
31
}
32
33
func (t *CopyTask) GetName() string {
34
return fmt.Sprintf("copy [%s](%s) to [%s](%s)", t.SrcStorageMp, t.SrcObjPath, t.DstStorageMp, t.DstDirPath)
35
}
36
37
func (t *CopyTask) GetStatus() string {
38
return t.Status
39
}
40
41
func (t *CopyTask) Run() error {
42
t.ReinitCtx()
43
t.ClearEndTime()
44
t.SetStartTime(time.Now())
45
defer func() { t.SetEndTime(time.Now()) }()
46
var err error
47
if t.srcStorage == nil {
48
t.srcStorage, err = op.GetStorageByMountPath(t.SrcStorageMp)
49
}
50
if t.dstStorage == nil {
51
t.dstStorage, err = op.GetStorageByMountPath(t.DstStorageMp)
52
}
53
if err != nil {
54
return errors.WithMessage(err, "failed get storage")
55
}
56
return copyBetween2Storages(t, t.srcStorage, t.dstStorage, t.SrcObjPath, t.DstDirPath)
57
}
58
59
var CopyTaskManager *tache.Manager[*CopyTask]
60
61
// Copy if in the same storage, call move method
62
// if not, add copy task
63
func _copy(ctx context.Context, srcObjPath, dstDirPath string, lazyCache ...bool) (task.TaskExtensionInfo, error) {
64
srcStorage, srcObjActualPath, err := op.GetStorageAndActualPath(srcObjPath)
65
if err != nil {
66
return nil, errors.WithMessage(err, "failed get src storage")
67
}
68
dstStorage, dstDirActualPath, err := op.GetStorageAndActualPath(dstDirPath)
69
if err != nil {
70
return nil, errors.WithMessage(err, "failed get dst storage")
71
}
72
// copy if in the same storage, just call driver.Copy
73
if srcStorage.GetStorage() == dstStorage.GetStorage() {
74
err = op.Copy(ctx, srcStorage, srcObjActualPath, dstDirActualPath, lazyCache...)
75
if !errors.Is(err, errs.NotImplement) && !errors.Is(err, errs.NotSupport) {
76
return nil, err
77
}
78
}
79
if ctx.Value(conf.NoTaskKey) != nil {
80
srcObj, err := op.Get(ctx, srcStorage, srcObjActualPath)
81
if err != nil {
82
return nil, errors.WithMessagef(err, "failed get src [%s] file", srcObjPath)
83
}
84
if !srcObj.IsDir() {
85
// copy file directly
86
link, _, err := op.Link(ctx, srcStorage, srcObjActualPath, model.LinkArgs{
87
Header: http.Header{},
88
})
89
if err != nil {
90
return nil, errors.WithMessagef(err, "failed get [%s] link", srcObjPath)
91
}
92
fs := stream.FileStream{
93
Obj: srcObj,
94
Ctx: ctx,
95
}
96
// any link provided is seekable
97
ss, err := stream.NewSeekableStream(fs, link)
98
if err != nil {
99
return nil, errors.WithMessagef(err, "failed get [%s] stream", srcObjPath)
100
}
101
return nil, op.Put(ctx, dstStorage, dstDirActualPath, ss, nil, false)
102
}
103
}
104
// not in the same storage
105
taskCreator, _ := ctx.Value("user").(*model.User)
106
t := &CopyTask{
107
TaskExtension: task.TaskExtension{
108
Creator: taskCreator,
109
},
110
srcStorage: srcStorage,
111
dstStorage: dstStorage,
112
SrcObjPath: srcObjActualPath,
113
DstDirPath: dstDirActualPath,
114
SrcStorageMp: srcStorage.GetStorage().MountPath,
115
DstStorageMp: dstStorage.GetStorage().MountPath,
116
}
117
CopyTaskManager.Add(t)
118
return t, nil
119
}
120
121
func copyBetween2Storages(t *CopyTask, srcStorage, dstStorage driver.Driver, srcObjPath, dstDirPath string) error {
122
t.Status = "getting src object"
123
srcObj, err := op.Get(t.Ctx(), srcStorage, srcObjPath)
124
if err != nil {
125
return errors.WithMessagef(err, "failed get src [%s] file", srcObjPath)
126
}
127
if srcObj.IsDir() {
128
t.Status = "src object is dir, listing objs"
129
objs, err := op.List(t.Ctx(), srcStorage, srcObjPath, model.ListArgs{})
130
if err != nil {
131
return errors.WithMessagef(err, "failed list src [%s] objs", srcObjPath)
132
}
133
for _, obj := range objs {
134
if utils.IsCanceled(t.Ctx()) {
135
return nil
136
}
137
srcObjPath := stdpath.Join(srcObjPath, obj.GetName())
138
dstObjPath := stdpath.Join(dstDirPath, srcObj.GetName())
139
CopyTaskManager.Add(&CopyTask{
140
TaskExtension: task.TaskExtension{
141
Creator: t.GetCreator(),
142
},
143
srcStorage: srcStorage,
144
dstStorage: dstStorage,
145
SrcObjPath: srcObjPath,
146
DstDirPath: dstObjPath,
147
SrcStorageMp: srcStorage.GetStorage().MountPath,
148
DstStorageMp: dstStorage.GetStorage().MountPath,
149
})
150
}
151
t.Status = "src object is dir, added all copy tasks of objs"
152
return nil
153
}
154
return copyFileBetween2Storages(t, srcStorage, dstStorage, srcObjPath, dstDirPath)
155
}
156
157
func copyFileBetween2Storages(tsk *CopyTask, srcStorage, dstStorage driver.Driver, srcFilePath, dstDirPath string) error {
158
srcFile, err := op.Get(tsk.Ctx(), srcStorage, srcFilePath)
159
if err != nil {
160
return errors.WithMessagef(err, "failed get src [%s] file", srcFilePath)
161
}
162
tsk.SetTotalBytes(srcFile.GetSize())
163
link, _, err := op.Link(tsk.Ctx(), srcStorage, srcFilePath, model.LinkArgs{
164
Header: http.Header{},
165
})
166
if err != nil {
167
return errors.WithMessagef(err, "failed get [%s] link", srcFilePath)
168
}
169
fs := stream.FileStream{
170
Obj: srcFile,
171
Ctx: tsk.Ctx(),
172
}
173
// any link provided is seekable
174
ss, err := stream.NewSeekableStream(fs, link)
175
if err != nil {
176
return errors.WithMessagef(err, "failed get [%s] stream", srcFilePath)
177
}
178
return op.Put(tsk.Ctx(), dstStorage, dstDirPath, ss, tsk.SetProgress, true)
179
}
180
181