/
zip.go
151 lines (139 loc) · 3.85 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
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
package pack
import (
"io"
"os"
"path/filepath"
"time"
"github.com/andybalholm/brotli"
"github.com/balibuild/bali/base"
"github.com/dsnet/compress/bzip2"
"github.com/klauspost/compress/zip"
"github.com/klauspost/compress/zstd"
)
// Zip
const (
ZipISVTX = 0x200
)
// Compression methods.
const (
Store uint16 = 0 // no compression
Deflate uint16 = 8 // DEFLATE compressed
BZIP2 uint16 = 12 // bzip2
LZMA uint16 = 14 //LZMA
ZSTD uint16 = 93 //see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT.
XZ uint16 = 95 //XZ
BROTLI uint16 = 121 // private
)
// ZipPacker todo
type ZipPacker struct {
zw *zip.Writer
FileMethod uint16 // zip filemethod
}
// NewZipPacker todo
func NewZipPacker(w io.Writer) *ZipPacker {
return &ZipPacker{zw: zip.NewWriter(w), FileMethod: Deflate}
}
// NewZipPackerEx todo
func NewZipPackerEx(w io.Writer, method uint16) *ZipPacker {
zp := NewZipPacker(w)
switch method {
case BZIP2:
zp.zw.RegisterCompressor(BZIP2, func(out io.Writer) (io.WriteCloser, error) {
return bzip2.NewWriter(out, nil)
})
zp.FileMethod = BZIP2
case ZSTD:
zp.zw.RegisterCompressor(ZSTD, func(out io.Writer) (io.WriteCloser, error) {
return zstd.NewWriter(out, zstd.WithEncoderLevel(zstd.SpeedBestCompression))
//return zstd.ZipCompressor()(out)
})
zp.FileMethod = ZSTD
case BROTLI:
zp.zw.RegisterCompressor(BROTLI, func(out io.Writer) (io.WriteCloser, error) {
return brotli.NewWriter(out), nil
})
zp.FileMethod = BROTLI
default:
}
return zp
}
// Close todo
func (zp *ZipPacker) Close() error {
if zp.zw == nil {
return nil
}
return zp.zw.Close()
}
func (zp *ZipPacker) SetComment(comment string) error {
return zp.zw.SetComment(comment)
}
// AddTargetLink create zip symlink
func (zp *ZipPacker) AddTargetLink(nameInArchive, linkName string) error {
var hdr zip.FileHeader
hdr.Modified = time.Now()
hdr.SetMode(0755 | os.ModeSymlink) // symlink
hdr.Name = filepath.ToSlash(nameInArchive)
writer, err := zp.zw.CreateHeader(&hdr)
if err != nil {
return base.ErrorCat(linkName, ": making header:", err.Error())
}
if _, err := writer.Write([]byte(filepath.ToSlash(linkName))); err != nil {
return base.ErrorCat(linkName, " writing symlink target: ", err.Error())
}
return nil
}
// AddFileEx todo
func (zp *ZipPacker) AddFileEx(src, nameInArchive string, exerights bool) error {
fi, err := os.Stat(src)
if err != nil {
return err
}
if fi.IsDir() {
header, err := zip.FileInfoHeader(fi)
if err != nil {
return base.ErrorCat(src, ": getting header: ", err.Error())
}
header.Name = base.StrCat(filepath.ToSlash(nameInArchive), "/")
header.Method = zip.Store
if _, err = zp.zw.CreateHeader(header); err != nil {
return base.ErrorCat(nameInArchive, ": making header:", err.Error())
}
return nil
}
header, err := zip.FileInfoHeader(fi)
if err != nil {
return base.ErrorCat(src, ": getting header: ", err.Error())
}
if exerights {
header.SetMode(header.Mode() | 0755)
}
header.Name = filepath.ToSlash(nameInArchive)
header.Method = zp.FileMethod
writer, err := zp.zw.CreateHeader(header)
if err != nil {
return base.ErrorCat(nameInArchive, ": making header:", err.Error())
}
if isSymlink(fi) {
linkTarget, err := os.Readlink(src)
if err != nil {
return base.ErrorCat(src, ": readlink: ", err.Error())
}
if _, err := writer.Write([]byte(filepath.ToSlash(linkTarget))); err != nil {
return base.ErrorCat(src, " writing symlink target: ", err.Error())
}
return nil
}
fd, err := os.Open(src)
if err != nil {
return base.ErrorCat(src, ": opening: ", err.Error())
}
defer fd.Close()
if _, err := io.Copy(writer, fd); err != nil {
return base.ErrorCat(src, ": copying contents: ", err.Error())
}
return nil
}
// AddFile file to zip packer
func (zp *ZipPacker) AddFile(src, nameInArchive string) error {
return zp.AddFileEx(src, nameInArchive, false)
}