1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.compressors.gzip;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.net.URI;
24 import java.net.URISyntaxException;
25 import java.nio.ByteBuffer;
26 import java.nio.ByteOrder;
27 import java.nio.charset.StandardCharsets;
28 import java.util.zip.CRC32;
29 import java.util.zip.Deflater;
30 import java.util.zip.GZIPInputStream;
31 import java.util.zip.GZIPOutputStream;
32
33 import org.apache.commons.compress.compressors.CompressorOutputStream;
34
35
36
37
38
39
40
41 public class GzipCompressorOutputStream extends CompressorOutputStream {
42
43
44 private static final int FNAME = 1 << 3;
45
46
47 private static final int FCOMMENT = 1 << 4;
48
49
50 private final OutputStream out;
51
52
53 private final Deflater deflater;
54
55
56 private final byte[] deflateBuffer;
57
58
59 private boolean closed;
60
61
62 private final CRC32 crc = new CRC32();
63
64
65
66
67
68
69
70 public GzipCompressorOutputStream(final OutputStream out) throws IOException {
71 this(out, new GzipParameters());
72 }
73
74
75
76
77
78
79
80
81
82
83 public GzipCompressorOutputStream(final OutputStream out, final GzipParameters parameters) throws IOException {
84 this.out = out;
85 this.deflater = new Deflater(parameters.getCompressionLevel(), true);
86 this.deflater.setStrategy(parameters.getDeflateStrategy());
87 this.deflateBuffer = new byte[parameters.getBufferSize()];
88 writeHeader(parameters);
89 }
90
91 @Override
92 public void close() throws IOException {
93 if (!closed) {
94 try {
95 finish();
96 } finally {
97 deflater.end();
98 out.close();
99 closed = true;
100 }
101 }
102 }
103
104 private void deflate() throws IOException {
105 final int length = deflater.deflate(deflateBuffer, 0, deflateBuffer.length);
106 if (length > 0) {
107 out.write(deflateBuffer, 0, length);
108 }
109 }
110
111
112
113
114
115
116
117 public void finish() throws IOException {
118 if (!deflater.finished()) {
119 deflater.finish();
120
121 while (!deflater.finished()) {
122 deflate();
123 }
124
125 writeTrailer();
126 }
127 }
128
129
130
131
132
133
134 @Override
135 public void flush() throws IOException {
136 out.flush();
137 }
138
139
140
141
142
143
144
145
146
147
148
149 private byte[] getBytes(final String string) throws IOException {
150 if (GzipUtils.GZIP_ENCODING.newEncoder().canEncode(string)) {
151 return string.getBytes(GzipUtils.GZIP_ENCODING);
152 }
153 try {
154 return new URI(null, null, string, null).toASCIIString().getBytes(StandardCharsets.US_ASCII);
155 } catch (final URISyntaxException e) {
156 throw new IOException(string, e);
157 }
158 }
159
160
161
162
163
164
165 @Override
166 public void write(final byte[] buffer) throws IOException {
167 write(buffer, 0, buffer.length);
168 }
169
170
171
172
173
174
175 @Override
176 public void write(final byte[] buffer, final int offset, final int length) throws IOException {
177 if (deflater.finished()) {
178 throw new IOException("Cannot write more data, the end of the compressed data stream has been reached");
179 }
180 if (length > 0) {
181 deflater.setInput(buffer, offset, length);
182
183 while (!deflater.needsInput()) {
184 deflate();
185 }
186
187 crc.update(buffer, offset, length);
188 }
189 }
190
191 @Override
192 public void write(final int b) throws IOException {
193 write(new byte[] { (byte) (b & 0xff) }, 0, 1);
194 }
195
196 private void writeHeader(final GzipParameters parameters) throws IOException {
197 final String fileName = parameters.getFileName();
198 final String comment = parameters.getComment();
199
200 final ByteBuffer buffer = ByteBuffer.allocate(10);
201 buffer.order(ByteOrder.LITTLE_ENDIAN);
202 buffer.putShort((short) GZIPInputStream.GZIP_MAGIC);
203 buffer.put((byte) Deflater.DEFLATED);
204 buffer.put((byte) ((fileName != null ? FNAME : 0) | (comment != null ? FCOMMENT : 0)));
205 buffer.putInt((int) (parameters.getModificationTime() / 1000));
206
207
208 final int compressionLevel = parameters.getCompressionLevel();
209 if (compressionLevel == Deflater.BEST_COMPRESSION) {
210 buffer.put((byte) 2);
211 } else if (compressionLevel == Deflater.BEST_SPEED) {
212 buffer.put((byte) 4);
213 } else {
214 buffer.put((byte) 0);
215 }
216
217 buffer.put((byte) parameters.getOperatingSystem());
218
219 out.write(buffer.array());
220
221 if (fileName != null) {
222 out.write(getBytes(fileName));
223 out.write(0);
224 }
225
226 if (comment != null) {
227 out.write(getBytes(comment));
228 out.write(0);
229 }
230 }
231
232 private void writeTrailer() throws IOException {
233 final ByteBuffer buffer = ByteBuffer.allocate(8);
234 buffer.order(ByteOrder.LITTLE_ENDIAN);
235 buffer.putInt((int) crc.getValue());
236 buffer.putInt(deflater.getTotalIn());
237
238 out.write(buffer.array());
239 }
240
241 }