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