/
compression.go
158 lines (131 loc) · 3.33 KB
/
compression.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
package pufferpanel
import (
"archive/tar"
"errors"
"github.com/klauspost/compress/zip"
"github.com/mholt/archiver/v3"
"io"
"os"
"path/filepath"
"strings"
)
const PathSeparator = string(os.PathSeparator)
type ExtractOptions struct {
FileServer FileServer
SourceFile string
TargetPath string
Filter string
SkipRoot bool
ForcedWalker Walker
}
func DetermineIfSingleRoot(sourceFile string) (bool, error) {
isSingleRoot := false
var rootName string
err := archiver.Walk(sourceFile, func(file archiver.File) (err error) {
name := getCompressedItemName(file)
if name == "" || name == PathSeparator {
return
}
root := strings.Split(name, PathSeparator)[0]
if rootName == "" {
rootName = root
return nil
}
if root != rootName {
return archiver.ErrStopWalk
}
return nil
})
if errors.Is(err, archiver.ErrStopWalk) {
isSingleRoot = false
}
return isSingleRoot, err
}
func Extract(fs FileServer, sourceFile, targetPath, filter string, skipRoot bool, forcedType Walker) error {
if fs != nil {
sourceFile = filepath.Join(fs.Prefix(), sourceFile)
}
if skipRoot {
var err error
skipRoot, err = DetermineIfSingleRoot(sourceFile)
if err != nil {
return err
}
}
if forcedType != nil {
return forcedType.Walk(sourceFile, walker(fs, targetPath, filter, skipRoot))
}
return archiver.Walk(sourceFile, walker(fs, targetPath, filter, skipRoot))
}
func Compress(fs FileServer, targetFile string, files []string) error {
if fs != nil {
p := fs.Prefix()
targetFile = filepath.Join(p, targetFile)
for k, v := range files {
files[k] = filepath.Join(p, v)
}
}
return archiver.Archive(files, targetFile)
}
func walker(fs FileServer, targetPath, filter string, skipRoot bool) archiver.WalkFunc {
return func(file archiver.File) (err error) {
path := getCompressedItemName(file)
if !CompareWildcard(file.Name(), filter) {
return
}
if skipRoot {
path = strings.Join(strings.Split(path, PathSeparator)[1:], PathSeparator)
}
parent := filepath.Join(targetPath, filepath.Dir(path))
path = filepath.Join(targetPath, path)
if file.Mode().IsDir() {
if fs != nil {
if err = fs.MkdirAll(path, 0755); err != nil {
return err
}
} else {
if err = os.MkdirAll(path, 0755); err != nil {
return err
}
}
} else if file.Mode().IsRegular() {
if fs != nil {
if err = fs.MkdirAll(parent, 0755); err != nil {
return err
}
} else {
if err = os.MkdirAll(parent, 0755); err != nil {
return err
}
}
var outFile *os.File
if fs != nil {
outFile, err = fs.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, file.Mode())
} else {
outFile, err = os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, file.Mode())
}
if err != nil {
return err
}
defer Close(outFile)
_, err = io.Copy(outFile, file.ReadCloser)
}
return
}
}
// getCompressedItemName Resolves headers in the event the wrapped interface fails
func getCompressedItemName(file archiver.File) string {
//For certain headers, the actual File interface uses the wrong value
//Example, ZIP gives the filename, not the full path
switch v := file.Header.(type) {
case zip.FileHeader:
return v.Name
case *tar.Header:
return v.Name
default:
return file.Name()
}
}
type Walker interface {
Walk(archive string, walkFn archiver.WalkFunc) error
}