-
Notifications
You must be signed in to change notification settings - Fork 5
/
open.go
142 lines (136 loc) · 2.99 KB
/
open.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
package vfs
import (
"archive/tar"
"archive/zip"
"bytes"
"compress/bzip2"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
// Zip returns an in-memory VFS initialized with the
// contents of the .zip file read from the given io.Reader.
// Since archive/zip requires an io.ReaderAt rather than an
// io.Reader, and a known size, Zip will read the whole file
// into memory and provide its own buffering if r does not
// implement io.ReaderAt or size is <= 0.
func Zip(r io.Reader, size int64) (VFS, error) {
rat, _ := r.(io.ReaderAt)
if rat == nil || size <= 0 {
data, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
rat = bytes.NewReader(data)
size = int64(len(data))
}
zr, err := zip.NewReader(rat, size)
if err != nil {
return nil, err
}
files := make(map[string]*File)
for _, file := range zr.File {
if file.Mode().IsDir() {
continue
}
f, err := file.Open()
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(f)
f.Close()
if err != nil {
return nil, err
}
files[file.Name] = &File{
Data: data,
Mode: file.Mode(),
ModTime: file.ModTime(),
}
}
return Map(files)
}
// Tar returns an in-memory VFS initialized with the
// contents of the .tar file read from the given io.Reader.
func Tar(r io.Reader) (VFS, error) {
files := make(map[string]*File)
tr := tar.NewReader(r)
for {
hdr, err := tr.Next()
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
if hdr.FileInfo().IsDir() {
continue
}
data, err := ioutil.ReadAll(tr)
if err != nil {
return nil, err
}
files[hdr.Name] = &File{
Data: data,
Mode: hdr.FileInfo().Mode(),
ModTime: hdr.ModTime,
}
}
return Map(files)
}
// TarGzip returns an in-memory VFS initialized with the
// contents of the .tar.gz file read from the given io.Reader.
func TarGzip(r io.Reader) (VFS, error) {
zr, err := gzip.NewReader(r)
if err != nil {
return nil, err
}
defer zr.Close()
return Tar(zr)
}
// TarBzip2 returns an in-memory VFS initialized with the
// contents of then .tar.bz2 file read from the given io.Reader.
func TarBzip2(r io.Reader) (VFS, error) {
bzr := bzip2.NewReader(r)
return Tar(bzr)
}
// Open returns an in-memory VFS initialized with the contents
// of the given filename, which must have one of the following
// extensions:
//
// - .zip
// - .tar
// - .tar.gz
// - .tar.bz2
func Open(filename string) (VFS, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
base := filepath.Base(filename)
ext := strings.ToLower(filepath.Ext(base))
nonExt := filename[:len(filename)-len(ext)]
if strings.ToLower(filepath.Ext(nonExt)) == ".tar" {
ext = ".tar" + ext
}
switch ext {
case ".zip":
st, err := f.Stat()
if err != nil {
return nil, err
}
return Zip(f, st.Size())
case ".tar":
return Tar(f)
case ".tar.gz":
return TarGzip(f)
case ".tar.bz2":
return TarBzip2(f)
}
return nil, fmt.Errorf("can't open a VFS from a %s file", ext)
}