forked from cookieo9/resources-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
zip.go
125 lines (112 loc) · 2.82 KB
/
zip.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
package resources
import (
"archive/zip"
"io"
"os"
"path"
)
type zipResource struct {
*zip.File
}
func (zr *zipResource) Path() string {
return zr.Name
}
func (zr *zipResource) Stat() (os.FileInfo, error) {
return zr.FileInfo(), nil
}
func (zr *zipResource) String() string {
return zr.Path()
}
type zipBundle struct {
file *os.File
rdr *zip.Reader
}
// Closes the ZipBundle's associated file, if
// created by OpenZip, otherwise a no-op
func (zb *zipBundle) Close() error {
if zb.file != nil {
return zb.file.Close()
}
return nil
}
// Open the resource at path in the ZipBundle for reading.
// Returns ErrNotFound if no file exists with that path.
func (zb *zipBundle) Open(path string) (io.ReadCloser, error) {
resource, err := zb.Find(path)
if err != nil {
return nil, err
}
return resource.Open()
}
// Finds the resource at path in the ZipBundle.
// Returns ErrNotFound if no file exists with that path.
func (zb *zipBundle) Find(path string) (Resource, error) {
for _, file := range zb.rdr.File {
if file.Name == path {
return &zipResource{file}, nil
}
}
return nil, ErrNotFound
}
// Finds all matching resources in the ZipBundle.
func (zb *zipBundle) Glob(pattern string) (resources []Resource, err error) {
for _, file := range zb.rdr.File {
if match, err := path.Match(pattern, file.Name); match {
resources = append(resources, &zipResource{file})
} else if err != nil {
return nil, err
}
}
return
}
// Lists all resources in the ZipBundle
func (zb *zipBundle) List() (list []Resource, err error) {
for _, file := range zb.rdr.File {
list = append(list, &zipResource{file})
}
return
}
// Opens a zipfile on disk as a bundle. You must call
// Close() to release the open file handle.
//
// Zip files opened as bundles implement the Bundle,
// Searcher, and Lister interfaces.
//
// If the file is in a known executable format,
// it is searched for an embedded zip file.
func OpenZip(path string) (Bundle, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
finfo, err := file.Stat()
if err != nil {
return nil, err
}
zb, err := OpenZipReader(file, finfo.Size())
if err != nil {
return nil, err
}
zb.(*zipBundle).file = file
return zb, nil
}
// Opens a zipfile specified by the given ReaderAt and size.
// Close() is a no-op on the returned structure, ie: you must
// close the reader's resource yourself if necessary.
//
// Zip files opened as bundles implement the Bundle,
// Searcher, and Lister interfaces.
//
// If the reader accesses data for a known executable format,
// it will be searched for an embedded zip file.
func OpenZipReader(rda io.ReaderAt, size int64) (Bundle, error) {
rdr, err := zip.NewReader(rda, size)
if err != nil {
rdr2, err2 := zipExeReader(rda, size)
if err2 != nil {
return nil, err
}
rdr = rdr2
}
return &zipBundle{rdr: rdr}, nil
}