Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/internal/archive/tool/helper.go
2358 views
1
package tool
2
3
import (
4
"fmt"
5
"io"
6
"io/fs"
7
"os"
8
stdpath "path"
9
"path/filepath"
10
"strings"
11
12
"github.com/alist-org/alist/v3/internal/model"
13
"github.com/alist-org/alist/v3/internal/stream"
14
)
15
16
type SubFile interface {
17
Name() string
18
FileInfo() fs.FileInfo
19
Open() (io.ReadCloser, error)
20
}
21
22
type CanEncryptSubFile interface {
23
IsEncrypted() bool
24
SetPassword(password string)
25
}
26
27
type ArchiveReader interface {
28
Files() []SubFile
29
}
30
31
func GenerateMetaTreeFromFolderTraversal(r ArchiveReader) (bool, []model.ObjTree) {
32
encrypted := false
33
dirMap := make(map[string]*model.ObjectTree)
34
for _, file := range r.Files() {
35
if encrypt, ok := file.(CanEncryptSubFile); ok && encrypt.IsEncrypted() {
36
encrypted = true
37
}
38
39
name := strings.TrimPrefix(file.Name(), "/")
40
var dir string
41
var dirObj *model.ObjectTree
42
isNewFolder := false
43
if !file.FileInfo().IsDir() {
44
// 先将 文件 添加到 所在的文件夹
45
dir = stdpath.Dir(name)
46
dirObj = dirMap[dir]
47
if dirObj == nil {
48
isNewFolder = dir != "."
49
dirObj = &model.ObjectTree{}
50
dirObj.IsFolder = true
51
dirObj.Name = stdpath.Base(dir)
52
dirObj.Modified = file.FileInfo().ModTime()
53
dirMap[dir] = dirObj
54
}
55
dirObj.Children = append(
56
dirObj.Children, &model.ObjectTree{
57
Object: *MakeModelObj(file.FileInfo()),
58
},
59
)
60
} else {
61
dir = strings.TrimSuffix(name, "/")
62
dirObj = dirMap[dir]
63
if dirObj == nil {
64
isNewFolder = dir != "."
65
dirObj = &model.ObjectTree{}
66
dirMap[dir] = dirObj
67
}
68
dirObj.IsFolder = true
69
dirObj.Name = stdpath.Base(dir)
70
dirObj.Modified = file.FileInfo().ModTime()
71
}
72
if isNewFolder {
73
// 将 文件夹 添加到 父文件夹
74
// 考虑压缩包仅记录文件的路径,不记录文件夹
75
// 循环创建所有父文件夹
76
parentDir := stdpath.Dir(dir)
77
for {
78
parentDirObj := dirMap[parentDir]
79
if parentDirObj == nil {
80
parentDirObj = &model.ObjectTree{}
81
if parentDir != "." {
82
parentDirObj.IsFolder = true
83
parentDirObj.Name = stdpath.Base(parentDir)
84
parentDirObj.Modified = file.FileInfo().ModTime()
85
}
86
dirMap[parentDir] = parentDirObj
87
}
88
parentDirObj.Children = append(parentDirObj.Children, dirObj)
89
90
parentDir = stdpath.Dir(parentDir)
91
if dirMap[parentDir] != nil {
92
break
93
}
94
dirObj = parentDirObj
95
}
96
}
97
}
98
if len(dirMap) > 0 {
99
return encrypted, dirMap["."].GetChildren()
100
} else {
101
return encrypted, nil
102
}
103
}
104
105
func MakeModelObj(file os.FileInfo) *model.Object {
106
return &model.Object{
107
Name: file.Name(),
108
Size: file.Size(),
109
Modified: file.ModTime(),
110
IsFolder: file.IsDir(),
111
}
112
}
113
114
type WrapFileInfo struct {
115
model.Obj
116
}
117
118
func DecompressFromFolderTraversal(r ArchiveReader, outputPath string, args model.ArchiveInnerArgs, up model.UpdateProgress) error {
119
var err error
120
files := r.Files()
121
if args.InnerPath == "/" {
122
for i, file := range files {
123
name := file.Name()
124
info := file.FileInfo()
125
if info.IsDir() {
126
var dirPath string
127
dirPath, err = SecureJoin(outputPath, name)
128
if err != nil {
129
return err
130
}
131
if err = os.MkdirAll(dirPath, 0700); err != nil {
132
return err
133
}
134
continue
135
}
136
if !info.Mode().IsRegular() {
137
return fmt.Errorf("%w: %s", ErrArchiveIllegalPath, name)
138
}
139
var dstPath string
140
dstPath, err = SecureJoin(outputPath, name)
141
if err != nil {
142
return err
143
}
144
if err = os.MkdirAll(filepath.Dir(dstPath), 0700); err != nil {
145
return err
146
}
147
err = _decompress(file, dstPath, args.Password, func(_ float64) {})
148
if err != nil {
149
return err
150
}
151
up(float64(i+1) * 100.0 / float64(len(files)))
152
}
153
} else {
154
innerPath := strings.TrimPrefix(args.InnerPath, "/")
155
innerBase := stdpath.Base(innerPath)
156
createdBaseDir := false
157
var baseDirPath string
158
for _, file := range files {
159
name := file.Name()
160
if name == innerPath {
161
info := file.FileInfo()
162
if info.IsDir() {
163
if !createdBaseDir {
164
baseDirPath, err = SecureJoin(outputPath, innerBase)
165
if err != nil {
166
return err
167
}
168
if err = os.MkdirAll(baseDirPath, 0700); err != nil {
169
return err
170
}
171
createdBaseDir = true
172
}
173
continue
174
}
175
if !info.Mode().IsRegular() {
176
return fmt.Errorf("%w: %s", ErrArchiveIllegalPath, name)
177
}
178
var dstPath string
179
dstPath, err = SecureJoin(outputPath, stdpath.Base(innerPath))
180
if err != nil {
181
return err
182
}
183
if err = os.MkdirAll(filepath.Dir(dstPath), 0700); err != nil {
184
return err
185
}
186
err = _decompress(file, dstPath, args.Password, up)
187
if err != nil {
188
return err
189
}
190
break
191
} else if strings.HasPrefix(name, innerPath+"/") {
192
if !createdBaseDir {
193
baseDirPath, err = SecureJoin(outputPath, innerBase)
194
if err != nil {
195
return err
196
}
197
err = os.MkdirAll(baseDirPath, 0700)
198
if err != nil {
199
return err
200
}
201
createdBaseDir = true
202
}
203
restPath := strings.TrimPrefix(name, innerPath+"/")
204
if restPath == "" || restPath == "." {
205
continue
206
}
207
info := file.FileInfo()
208
if info.IsDir() {
209
var dirPath string
210
dirPath, err = SecureJoin(baseDirPath, restPath)
211
if err != nil {
212
return err
213
}
214
if err = os.MkdirAll(dirPath, 0700); err != nil {
215
return err
216
}
217
continue
218
}
219
if !info.Mode().IsRegular() {
220
return fmt.Errorf("%w: %s", ErrArchiveIllegalPath, name)
221
}
222
var dstPath string
223
dstPath, err = SecureJoin(baseDirPath, restPath)
224
if err != nil {
225
return err
226
}
227
if err = os.MkdirAll(filepath.Dir(dstPath), 0700); err != nil {
228
return err
229
}
230
err = _decompress(file, dstPath, args.Password, func(_ float64) {})
231
if err != nil {
232
return err
233
}
234
}
235
}
236
}
237
return nil
238
}
239
240
func _decompress(file SubFile, dstPath, password string, up model.UpdateProgress) error {
241
if encrypt, ok := file.(CanEncryptSubFile); ok && encrypt.IsEncrypted() {
242
encrypt.SetPassword(password)
243
}
244
rc, err := file.Open()
245
if err != nil {
246
return err
247
}
248
defer func() { _ = rc.Close() }()
249
f, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
250
if err != nil {
251
return err
252
}
253
defer func() { _ = f.Close() }()
254
_, err = io.Copy(f, &stream.ReaderUpdatingProgress{
255
Reader: &stream.SimpleReaderWithSize{
256
Reader: rc,
257
Size: file.FileInfo().Size(),
258
},
259
UpdateProgress: up,
260
})
261
if err != nil {
262
return err
263
}
264
return nil
265
}
266
267