-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path第 12.02.03、compress_gzip 包
243 lines (165 loc) · 6.75 KB
/
第 12.02.03、compress_gzip 包
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
总览
gzip 包实现了读取和写入 gzip 格式的压缩文件,如 RFC 1952 中定义的一样
EG.【WriterReader】
var buf bytes.Buffer
zw := gzip.NewWriter(&buf)
// 设置头(header)字段为可选项
zw.Name = "a-new-hope.txt"
zw.Comment = "an epic space opera by George Lucas"
zw.ModTime = time.Date(1977, time.May, 25, 0, 0, 0, 0, time.UTC)
_, err := zw.Write([]byte("A long time ago in a galaxy far, far away..."))
if err != nil {
log.Fatal(err)
}
if err := zw.Close(); err != nil {
log.Fatal(err)
}
zr, err := gzip.NewReader(&buf)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Name: %s\nComment: %s\nModTime: %s\n\n", zr.Name, zr.Comment, zr.ModTime.UTC())
if _, err := io.Copy(os.Stdout, zr); err != nil {
log.Fatal(err)
}
if err := zr.Close(); err != nil {
log.Fatal(err)
}
常量
这些常量从 flate 包拷贝而来,这样导入 "compres/gzip" 包就不用同时导入 "compress/flate" 包了
const (
NoCompression = flate.NoCompression
BestSpeed = flate.BestSpeed
BestCompression = flate.BestCompression
DefaultCompression = flate.DefaultCompression
HuffmanOnly = flate.HuffmanOnly
)
变量
var (
// ErrChecksum 在读取具有无效校验和的 GZIP 数据时被引发
ErrChecksum = errors.New("gzip: invalid checksum")
// ErrHeader 在读取具有无效头(header)数据
ErrHeader = errors.New("gzip: invalid header")
)
Header struct 类型
gzip 文件记录了一个存储了压缩文件的元数据的头数据
这个头数据将会作为 Writer 和 Reader 结构体的字段被导出
基于 GZIP 文件格式的限制,
字符串必须是 UTF-8 编码,且仅能包含 U+0001 至 U+00FF 的 Unicode 码点
type Header struct {
Comment string // 注释
Extra []byte // “附加数据”
ModTime time.Time // 修改时间
Name string // 文件名
OS byte // 操作系统类型
}
Reader struct 类型
一个 Reader 是一个 io.Reader,其可以从一个 gzip 格式的压缩文件中取回未压缩的数据
一般来说,一个 gzip 文件可以由一系列 gzip 文件构成,每个 gzip 组分都有各自的头数据
Reader 的 Read 会返回每个 gzip 组分的未压缩数据
仅有第一个头数据被记录在 Reader 的字段中
gzip 文件存储了未压缩数据的长度和校验和
当 Read 读到了未压缩数据,但并非是预料中的长度或校验和时,就会返回 ErrChecksum
客户端应该视 Read 返回的数据为位确定的,直到收到 io.EOF 标记的数据结尾
type Reader struct {
Header // 在 NewReader 或 Reader.Reset 之后有效
// 包含了过滤或者未导出的字段
}
NewReader(r io.Reader) (*Reader, error)
创建一个新的 Reader 从给定的 reader 读取数据
若 r 没有同样实现 io.ByteReader,解压器也许会从 r 中读取比所需的更多的数据
在完成后,是调用者的责任对 Reader 调用 Close
返回的 Reader 的 Reader.Header 将是有效的
(z *Reader) Close() error
关闭 Reader
它并不会关闭底层的 io.Reader
为了让 GZIP 校验和被验证,reader 必须王权被消耗,直到出现 io.EOF
(z *Reader) Multistream(ok bool)
控制 reader 是否支持多重流文件
若启用(默认值),Reader 希望输入时一系列独立的 gzip 压缩过的数据流,
每个都有自己的头数据和结尾,且以 EOF 结尾
效果就是,gzip 压缩文件的序列的结合,等价于序列的结合的 gzip 数据
这是 gzip 头数据的标准行为
调用 Multistream(false) 禁用了这种行为;
禁用这种行为,在读取文件格式时,区分独立 gzip 数据流或混合 gzip 数据流与其他数据流时很有效
在这种模式下,当 Reader 遇到了数据流的结尾,Read 返回 io.EOF
若底层的 reader 满足了 io.ByteReader,它将在 gzip 数据流之后立刻离开
要开始另一个流,在 z.Multistream(false) 后调用 z.Reset(r)
若没有下一个流,则 z.Reset(r) 将返回 io.EOF
EG.
var buf bytes.Buffer
zw := gzip.NewWriter(&buf)
var files = []struct {
name string
comment string
modTime time.Time
data string
}{
{"file-1.txt", "file-header-1", time.Date(2006, time.February, 1, 3, 4, 5, 0, time.UTC), "Hello Gophers - 1"},
{"file-2.txt", "file-header-2", time.Date(2007, time.March, 2, 4, 5, 6, 1, time.UTC), "Hello Gophers - 2"},
}
for _, file := range files {
zw.Name = file.name
zw.Comment = file.comment
zw.ModTime = file.modTime
if _, err := zw.Write([]byte(file.data)); err != nil {
log.Fatal(err)
}
if err := zw.Close(); err != nil {
log.Fatal(err)
}
zw.Reset(&buf)
}
zr, err := gzip.NewReader(&buf)
if err != nil {
log.Fatal(err)
}
for {
zr.Multistream(false)
fmt.Printf("Name: %s\nComment: %s\nModTime: %s\n\n", zr.Name, zr.Comment, zr.ModTime.UTC())
if _, err := io.Copy(os.Stdout, zr); err != nil {
log.Fatal(err)
}
fmt.Print("\n\n")
err = zr.Reset(&buf)
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
}
if err := zr.Close(); err != nil {
log.Fatal(err)
}
(z *Reader) Read(p []byte) (n int, err error)
实现了 io.Reader,从底层 Reader 中读取未压缩的字节
(z *Reader) Reset(r io.Reader) error
取消 Reader z 的状态,让它的状态等价于从 NewReader 获得的原始状态,但从 r 读取数据
这允许复用一个 Reader,而非重新分配一个
Writer struct 类型
一个 Writer 是一个 io.WriterCloser
写入 Writer 的数据将会被压缩,并写入 w 中
当完成时,对 WriteCloser 调用 Close,是调用者的责任
直到调用 Close,写u人的数据都可能在缓存中,而没有被刷写
希望设置 Writer.Header 的调用者,必须在第一次调用 Write/Flush/Close 前设置
NewWriterLevel(w io.Writer, level int) (*Writer, error)
与 NewWriter 类似,但指定压缩等级,而非假设为 DefaultCompression
压缩等级可以为 DefaultCompression, NoCompression, HuffmanOnly,
或任何包含在 BestSpeed 与 BestCompression 之间的整数
若等级有效,则返回的错误为 nil
(z *Writer) Close() error
关闭一个 Writer,通过刷写任何未写入的数据至底层的 io.Writer,并写上 GZIP 脚标(footer)
它并不会关闭底层的 io.Writer
(z *Writer) Flush() error
刷写任何等待中的数据至底层的 writer
它主要常用于压缩网络协议中,来保证远程 reader 具有足够的数据重构一个包
本方法并不会返回知道数据被写入
若底层的 writer 返回了一个错误,本方法就返回那个错误
(z *Writer) Reset(w io.Writer)
取消 Writer z 的状体,让它的状态等价于从 NewWriter 或 NewWriterLevel 获得的原始状态,
但写入 w 中
这允许复用一个 Writer 而非重新分配一个
(z *Writer) Write(p []byte) (int, error)
将 p 的压缩形式写入底层的 io.Writer
直到 Writer 关闭,压缩的数据都不是必须被刷写的