-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathunzip.go
147 lines (126 loc) · 3.45 KB
/
unzip.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
package unzip
import (
"archive/zip"
"fmt"
"github.com/golang-infrastructure/go-pointer"
"sync"
)
// Unzip 用于封装解压缩的逻辑
type Unzip struct {
options *Options
}
// New 从选项创建一个解压缩组件
func New(options *Options) *Unzip {
return &Unzip{
options: options,
}
}
// FileHandler 用来处理解压出来的文件
type FileHandler func(file *File, options *Options) error
// SafeTraversal 安全的遍历压缩文件,遇到非法的或者错误的压缩文件时会自动检测报错
func (x *Unzip) SafeTraversal(handler FileHandler) (err error) {
return x.Traversal(func(file *File, options *Options) error {
if IsZipSlip(x.options.DestinationDirectory, file.Name) {
return fmt.Errorf("zip slip, deny")
}
return handler(file, x.options)
})
}
// Traversal 遍历zip文件
func (x *Unzip) Traversal(handler FileHandler) (err error) {
// 参数检查
if x.options.SourceZipFile == "" {
return ErrSourceZipFileEmpty
} else if x.options.WorkerNum == nil || pointer.FromPointer(x.options.WorkerNum) <= 0 {
return ErrWorkerNumInvalid
}
// 打开压缩文件
var r *zip.ReadCloser
r, err = zip.OpenReader(x.options.SourceZipFile)
if err != nil {
return err
}
defer func() {
// 如果有其它错误的话,优先返回其它错误的类型
localError := r.Close()
if err == nil {
err = localError
}
}()
// 初始化文件队列
fileChannel := x.makeZipFileChannel(r.File)
// 并发处理压缩文件中的每个文件
var wg sync.WaitGroup
for i := 0; i < pointer.FromPointer(x.options.WorkerNum); i++ {
wg.Add(1)
go func() {
defer wg.Done()
for file := range fileChannel {
// TODO 错误收集
_ = handler(file, x.options)
}
}()
}
wg.Wait()
return nil
}
// makeZipFileChannel 把zip数组转换为chan队列
func (x *Unzip) makeZipFileChannel(files []*zip.File) chan *File {
fileChannel := make(chan *File, len(files))
for _, f := range files {
fileChannel <- &File{
File: f,
}
}
close(fileChannel)
return fileChannel
}
//// CheckZipSlip 检查zip文件是否有zip slip漏洞
//func (x *Unzip) CheckZipSlip() []*zip.File {
//
// x.Traversal(func(file *zip.File, options *Options) error {
// if x.IsZipSlip(file) {
//
// }
// })
//}
// Unzip 解压文件到给定的目录
func (x *Unzip) Unzip() error {
// 参数检查
if x.options.WorkerNum == nil || pointer.FromPointer(x.options.WorkerNum) <= 0 {
return ErrWorkerNumInvalid
} else if x.options.SourceZipFile == "" {
return ErrSourceZipFileEmpty
} else if x.options.DestinationDirectory == "" {
return ErrDestinationDirectoryEmpty
}
// 遍历压缩文件中的每个Entry,依此保存到磁盘上
return x.SafeTraversal(func(file *File, options *Options) error {
return file.Unzip(x.options.DestinationDirectory)
})
}
// Files 读取压缩包中的所有文件和内容并返回,注意这个方法会实际解压缩文件,确保你机器的资源是足够的
func (x *Unzip) Files() (fileSlice []*File, err error) {
var r *zip.ReadCloser
r, err = zip.OpenReader(x.options.SourceZipFile)
if err != nil {
return nil, err
}
defer func() {
// 如果有其它错误的话,优先返回其它错误的类型
localError := r.Close()
if localError != nil && err == nil {
err = localError
}
}()
fileSlice = make([]*File, len(r.File))
for i, f := range r.File {
file := File{File: f}
_, err := file.ReadBytes()
if err != nil {
return nil, err
}
fileSlice[i] = &file
}
return fileSlice, nil
}