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.nio.ByteBuffer;
24 import java.nio.ByteOrder;
25 import java.nio.charset.Charset;
26 import java.util.zip.CRC32;
27 import java.util.zip.Deflater;
28 import java.util.zip.GZIPOutputStream;
29
30 import org.apache.commons.compress.compressors.CompressorOutputStream;
31
32
33
34
35
36
37
38 public class GzipCompressorOutputStream extends CompressorOutputStream<OutputStream> {
39
40
41 private final Deflater deflater;
42
43
44 private final byte[] deflateBuffer;
45
46
47 private final CRC32 crc = new CRC32();
48
49
50
51
52
53
54
55 public GzipCompressorOutputStream(final OutputStream out) throws IOException {
56 this(out, new GzipParameters());
57 }
58
59
60
61
62
63
64
65
66
67 public GzipCompressorOutputStream(final OutputStream out, final GzipParameters parameters) throws IOException {
68 super(out);
69 this.deflater = new Deflater(parameters.getCompressionLevel(), true);
70 this.deflater.setStrategy(parameters.getDeflateStrategy());
71 this.deflateBuffer = new byte[parameters.getBufferSize()];
72 writeMemberHeader(parameters);
73 }
74
75 @Override
76 public void close() throws IOException {
77 if (!isClosed()) {
78 try {
79 finish();
80 } finally {
81 deflater.end();
82 super.close();
83 }
84 }
85 }
86
87 private void deflate() throws IOException {
88 final int length = deflater.deflate(deflateBuffer, 0, deflateBuffer.length);
89 if (length > 0) {
90 out.write(deflateBuffer, 0, length);
91 }
92 }
93
94
95
96
97
98
99
100 @Override
101 public void finish() throws IOException {
102 if (!deflater.finished()) {
103 deflater.finish();
104 while (!deflater.finished()) {
105 deflate();
106 }
107 writeMemberTrailer();
108 deflater.reset();
109 }
110 }
111
112
113
114
115
116
117 @Override
118 public void write(final byte[] buffer) throws IOException {
119 write(buffer, 0, buffer.length);
120 }
121
122
123
124
125
126
127 @Override
128 public void write(final byte[] buffer, final int offset, final int length) throws IOException {
129 checkOpen();
130 if (deflater.finished()) {
131 throw new IOException("Cannot write more data, the end of the compressed data stream has been reached.");
132 }
133 if (length > 0) {
134 deflater.setInput(buffer, offset, length);
135 while (!deflater.needsInput()) {
136 deflate();
137 }
138 crc.update(buffer, offset, length);
139 }
140 }
141
142 @Override
143 public void write(final int b) throws IOException {
144 write(new byte[] { (byte) (b & 0xff) }, 0, 1);
145 }
146
147
148
149
150
151
152
153
154 private void writeC(final String value, final Charset charset) throws IOException {
155 if (value != null) {
156 final byte[] ba = value.getBytes(charset);
157 out.write(ba);
158 out.write(0);
159 crc.update(ba);
160 crc.update(0);
161 }
162 }
163
164 private void writeMemberHeader(final GzipParameters parameters) throws IOException {
165 final String fileName = parameters.getFileName();
166 final String comment = parameters.getComment();
167 final byte[] extra = parameters.getExtraField() != null ? parameters.getExtraField().toByteArray() : null;
168 final ByteBuffer buffer = ByteBuffer.allocate(10);
169 buffer.order(ByteOrder.LITTLE_ENDIAN);
170 buffer.put((byte) GzipUtils.ID1);
171 buffer.put((byte) GzipUtils.ID2);
172 buffer.put((byte) Deflater.DEFLATED);
173 buffer.put((byte) ((extra != null ? GzipUtils.FEXTRA : 0)
174 | (fileName != null ? GzipUtils.FNAME : 0)
175 | (comment != null ? GzipUtils.FCOMMENT : 0)
176 | (parameters.getHeaderCRC() ? GzipUtils.FHCRC : 0)
177 ));
178 buffer.putInt((int) parameters.getModificationInstant().getEpochSecond());
179
180 final int compressionLevel = parameters.getCompressionLevel();
181 if (compressionLevel == Deflater.BEST_COMPRESSION) {
182 buffer.put(GzipUtils.XFL_MAX_COMPRESSION);
183 } else if (compressionLevel == Deflater.BEST_SPEED) {
184 buffer.put(GzipUtils.XFL_MAX_SPEED);
185 } else {
186 buffer.put(GzipUtils.XFL_UNKNOWN);
187 }
188 buffer.put((byte) parameters.getOperatingSystem());
189 out.write(buffer.array());
190 crc.update(buffer.array());
191 if (extra != null) {
192 out.write(extra.length & 0xff);
193 out.write(extra.length >>> 8 & 0xff);
194 out.write(extra);
195 crc.update(extra.length & 0xff);
196 crc.update(extra.length >>> 8 & 0xff);
197 crc.update(extra);
198 }
199 writeC(fileName, parameters.getFileNameCharset());
200 writeC(comment, parameters.getFileNameCharset());
201 if (parameters.getHeaderCRC()) {
202 final int v = (int) crc.getValue() & 0xffff;
203 out.write(v & 0xff);
204 out.write(v >>> 8 & 0xff);
205 }
206 crc.reset();
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220 private void writeMemberTrailer() throws IOException {
221 final ByteBuffer buffer = ByteBuffer.allocate(8);
222 buffer.order(ByteOrder.LITTLE_ENDIAN);
223 buffer.putInt((int) crc.getValue());
224 buffer.putInt(deflater.getTotalIn());
225 out.write(buffer.array());
226 }
227
228 }