forked from cloudreve/Cloudreve
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfolder.go
324 lines (267 loc) · 7.83 KB
/
folder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
package model
import (
"errors"
"path"
"time"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/jinzhu/gorm"
)
// Folder 目录
type Folder struct {
// 表字段
gorm.Model
Name string `gorm:"unique_index:idx_only_one_name"`
ParentID *uint `gorm:"index:parent_id;unique_index:idx_only_one_name"`
OwnerID uint `gorm:"index:owner_id"`
// 数据库忽略字段
Position string `gorm:"-"`
}
// Create 创建目录
func (folder *Folder) Create() (uint, error) {
if err := DB.FirstOrCreate(folder, *folder).Error; err != nil {
folder.Model = gorm.Model{}
err2 := DB.First(folder, *folder).Error
return folder.ID, err2
}
return folder.ID, nil
}
// GetChild 返回folder下名为name的子目录,不存在则返回错误
func (folder *Folder) GetChild(name string) (*Folder, error) {
var resFolder Folder
err := DB.
Where("parent_id = ? AND owner_id = ? AND name = ?", folder.ID, folder.OwnerID, name).
First(&resFolder).Error
// 将子目录的路径传递下去
if err == nil {
resFolder.Position = path.Join(folder.Position, folder.Name)
}
return &resFolder, err
}
// TraceRoot 向上递归查找父目录
func (folder *Folder) TraceRoot() error {
if folder.ParentID == nil {
return nil
}
var parentFolder Folder
err := DB.
Where("id = ? AND owner_id = ?", folder.ParentID, folder.OwnerID).
First(&parentFolder).Error
if err == nil {
err := parentFolder.TraceRoot()
folder.Position = path.Join(parentFolder.Position, parentFolder.Name)
return err
}
return err
}
// GetChildFolder 查找子目录
func (folder *Folder) GetChildFolder() ([]Folder, error) {
var folders []Folder
result := DB.Where("parent_id = ?", folder.ID).Find(&folders)
if result.Error == nil {
for i := 0; i < len(folders); i++ {
folders[i].Position = path.Join(folder.Position, folder.Name)
}
}
return folders, result.Error
}
// GetRecursiveChildFolder 查找所有递归子目录,包括自身
func GetRecursiveChildFolder(dirs []uint, uid uint, includeSelf bool) ([]Folder, error) {
folders := make([]Folder, 0, len(dirs))
var err error
var parFolders []Folder
result := DB.Where("owner_id = ? and id in (?)", uid, dirs).Find(&parFolders)
if result.Error != nil {
return folders, err
}
// 整理父目录的ID
var parentIDs = make([]uint, 0, len(parFolders))
for _, folder := range parFolders {
parentIDs = append(parentIDs, folder.ID)
}
if includeSelf {
// 合并至最终结果
folders = append(folders, parFolders...)
}
parFolders = []Folder{}
// 递归查询子目录,最大递归65535次
for i := 0; i < 65535; i++ {
result = DB.Where("owner_id = ? and parent_id in (?)", uid, parentIDs).Find(&parFolders)
// 查询结束条件
if len(parFolders) == 0 {
break
}
// 整理父目录的ID
parentIDs = make([]uint, 0, len(parFolders))
for _, folder := range parFolders {
parentIDs = append(parentIDs, folder.ID)
}
// 合并至最终结果
folders = append(folders, parFolders...)
parFolders = []Folder{}
}
return folders, err
}
// DeleteFolderByIDs 根据给定ID批量删除目录记录
func DeleteFolderByIDs(ids []uint) error {
result := DB.Where("id in (?)", ids).Unscoped().Delete(&Folder{})
return result.Error
}
// GetFoldersByIDs 根据ID和用户查找所有目录
func GetFoldersByIDs(ids []uint, uid uint) ([]Folder, error) {
var folders []Folder
result := DB.Where("id in (?) AND owner_id = ?", ids, uid).Find(&folders)
return folders, result.Error
}
// MoveOrCopyFileTo 将此目录下的files移动或复制至dstFolder,
// 返回此操作新增的容量
func (folder *Folder) MoveOrCopyFileTo(files []uint, dstFolder *Folder, isCopy bool) (uint64, error) {
// 已复制文件的总大小
var copiedSize uint64
if isCopy {
// 检索出要复制的文件
var originFiles = make([]File, 0, len(files))
if err := DB.Where(
"id in (?) and user_id = ? and folder_id = ?",
files,
folder.OwnerID,
folder.ID,
).Find(&originFiles).Error; err != nil {
return 0, err
}
// 复制文件记录
for _, oldFile := range originFiles {
if !oldFile.CanCopy() {
util.Log().Warning("Cannot copy file %q because it's being uploaded now, skipping...", oldFile.Name)
continue
}
oldFile.Model = gorm.Model{}
oldFile.FolderID = dstFolder.ID
oldFile.UserID = dstFolder.OwnerID
if err := DB.Create(&oldFile).Error; err != nil {
return copiedSize, err
}
copiedSize += oldFile.Size
}
} else {
// 更改顶级要移动文件的父目录指向
err := DB.Model(File{}).Where(
"id in (?) and user_id = ? and folder_id = ?",
files,
folder.OwnerID,
folder.ID,
).
Update(map[string]interface{}{
"folder_id": dstFolder.ID,
}).
Error
if err != nil {
return 0, err
}
}
return copiedSize, nil
}
// CopyFolderTo 将此目录及其子目录及文件递归复制至dstFolder
// 返回此操作新增的容量
func (folder *Folder) CopyFolderTo(folderID uint, dstFolder *Folder) (size uint64, err error) {
// 列出所有子目录
subFolders, err := GetRecursiveChildFolder([]uint{folderID}, folder.OwnerID, true)
if err != nil {
return 0, err
}
// 抽离所有子目录的ID
var subFolderIDs = make([]uint, len(subFolders))
for key, value := range subFolders {
subFolderIDs[key] = value.ID
}
// 复制子目录
var newIDCache = make(map[uint]uint)
for _, folder := range subFolders {
// 新的父目录指向
var newID uint
// 顶级目录直接指向新的目的目录
if folder.ID == folderID {
newID = dstFolder.ID
} else if IDCache, ok := newIDCache[*folder.ParentID]; ok {
newID = IDCache
} else {
util.Log().Warning("Failed to get parent folder %q", *folder.ParentID)
return size, errors.New("Failed to get parent folder")
}
// 插入新的目录记录
oldID := folder.ID
folder.Model = gorm.Model{}
folder.ParentID = &newID
folder.OwnerID = dstFolder.OwnerID
if err = DB.Create(&folder).Error; err != nil {
return size, err
}
// 记录新的ID以便其子目录使用
newIDCache[oldID] = folder.ID
}
// 复制文件
var originFiles = make([]File, 0, len(subFolderIDs))
if err := DB.Where(
"user_id = ? and folder_id in (?)",
folder.OwnerID,
subFolderIDs,
).Find(&originFiles).Error; err != nil {
return 0, err
}
// 复制文件记录
for _, oldFile := range originFiles {
if !oldFile.CanCopy() {
util.Log().Warning("Cannot copy file %q because it's being uploaded now, skipping...", oldFile.Name)
continue
}
oldFile.Model = gorm.Model{}
oldFile.FolderID = newIDCache[oldFile.FolderID]
oldFile.UserID = dstFolder.OwnerID
if err := DB.Create(&oldFile).Error; err != nil {
return size, err
}
size += oldFile.Size
}
return size, nil
}
// MoveFolderTo 将folder目录下的dirs子目录复制或移动到dstFolder,
// 返回此过程中增加的容量
func (folder *Folder) MoveFolderTo(dirs []uint, dstFolder *Folder) error {
// 如果目标位置为待移动的目录,会导致 parent 为自己
// 造成死循环且无法被除搜索以外的组件展示
if folder.OwnerID == dstFolder.OwnerID && util.ContainsUint(dirs, dstFolder.ID) {
return errors.New("cannot move a folder into itself")
}
// 更改顶级要移动目录的父目录指向
err := DB.Model(Folder{}).Where(
"id in (?) and owner_id = ? and parent_id = ?",
dirs,
folder.OwnerID,
folder.ID,
).Update(map[string]interface{}{
"parent_id": dstFolder.ID,
}).Error
return err
}
// Rename 重命名目录
func (folder *Folder) Rename(new string) error {
return DB.Model(&folder).UpdateColumn("name", new).Error
}
/*
实现 FileInfo.FileInfo 接口
TODO 测试
*/
func (folder *Folder) GetName() string {
return folder.Name
}
func (folder *Folder) GetSize() uint64 {
return 0
}
func (folder *Folder) ModTime() time.Time {
return folder.UpdatedAt
}
func (folder *Folder) IsDir() bool {
return true
}
func (folder *Folder) GetPosition() string {
return folder.Position
}