1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.compress.archivers.zip;
18
19 import java.io.Closeable;
20 import java.io.File;
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.nio.file.Path;
25 import java.util.Iterator;
26 import java.util.Queue;
27 import java.util.concurrent.ConcurrentLinkedQueue;
28 import java.util.concurrent.atomic.AtomicBoolean;
29 import java.util.zip.Deflater;
30
31 import org.apache.commons.compress.parallel.FileBasedScatterGatherBackingStore;
32 import org.apache.commons.compress.parallel.ScatterGatherBackingStore;
33 import org.apache.commons.compress.utils.BoundedInputStream;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public class ScatterZipOutputStream implements Closeable {
51
52 private static final class CompressedEntry {
53 final ZipArchiveEntryRequest zipArchiveEntryRequest;
54 final long crc;
55 final long compressedSize;
56 final long size;
57
58 CompressedEntry(final ZipArchiveEntryRequest zipArchiveEntryRequest, final long crc, final long compressedSize, final long size) {
59 this.zipArchiveEntryRequest = zipArchiveEntryRequest;
60 this.crc = crc;
61 this.compressedSize = compressedSize;
62 this.size = size;
63 }
64
65
66
67
68
69
70
71 public ZipArchiveEntry transferToArchiveEntry() {
72 final ZipArchiveEntry entry = zipArchiveEntryRequest.getZipArchiveEntry();
73 entry.setCompressedSize(compressedSize);
74 entry.setSize(size);
75 entry.setCrc(crc);
76 entry.setMethod(zipArchiveEntryRequest.getMethod());
77 return entry;
78 }
79 }
80
81 public static class ZipEntryWriter implements Closeable {
82 private final Iterator<CompressedEntry> itemsIterator;
83 private final InputStream itemsIteratorData;
84
85 public ZipEntryWriter(final ScatterZipOutputStream scatter) throws IOException {
86 scatter.backingStore.closeForWriting();
87 itemsIterator = scatter.items.iterator();
88 itemsIteratorData = scatter.backingStore.getInputStream();
89 }
90
91 @Override
92 public void close() throws IOException {
93 if (itemsIteratorData != null) {
94 itemsIteratorData.close();
95 }
96 }
97
98 public void writeNextZipEntry(final ZipArchiveOutputStream target) throws IOException {
99 final CompressedEntry compressedEntry = itemsIterator.next();
100 try (BoundedInputStream rawStream = new BoundedInputStream(itemsIteratorData, compressedEntry.compressedSize)) {
101 target.addRawArchiveEntry(compressedEntry.transferToArchiveEntry(), rawStream);
102 }
103 }
104 }
105
106
107
108
109
110
111
112
113 public static ScatterZipOutputStream fileBased(final File file) throws FileNotFoundException {
114 return pathBased(file.toPath(), Deflater.DEFAULT_COMPRESSION);
115 }
116
117
118
119
120
121
122
123
124
125 public static ScatterZipOutputStream fileBased(final File file, final int compressionLevel) throws FileNotFoundException {
126 return pathBased(file.toPath(), compressionLevel);
127 }
128
129
130
131
132
133
134
135
136
137 public static ScatterZipOutputStream pathBased(final Path path) throws FileNotFoundException {
138 return pathBased(path, Deflater.DEFAULT_COMPRESSION);
139 }
140
141
142
143
144
145
146
147
148
149
150 public static ScatterZipOutputStream pathBased(final Path path, final int compressionLevel) throws FileNotFoundException {
151 final ScatterGatherBackingStore bs = new FileBasedScatterGatherBackingStore(path);
152
153 final StreamCompressor sc = StreamCompressor.create(compressionLevel, bs);
154 return new ScatterZipOutputStream(bs, sc);
155 }
156
157 private final Queue<CompressedEntry> items = new ConcurrentLinkedQueue<>();
158
159 private final ScatterGatherBackingStore backingStore;
160
161 private final StreamCompressor streamCompressor;
162
163 private final AtomicBoolean isClosed = new AtomicBoolean();
164
165 private ZipEntryWriter zipEntryWriter;
166
167 public ScatterZipOutputStream(final ScatterGatherBackingStore backingStore, final StreamCompressor streamCompressor) {
168 this.backingStore = backingStore;
169 this.streamCompressor = streamCompressor;
170 }
171
172
173
174
175
176
177
178 public void addArchiveEntry(final ZipArchiveEntryRequest zipArchiveEntryRequest) throws IOException {
179 try (InputStream payloadStream = zipArchiveEntryRequest.getPayloadStream()) {
180 streamCompressor.deflate(payloadStream, zipArchiveEntryRequest.getMethod());
181 }
182 items.add(new CompressedEntry(zipArchiveEntryRequest, streamCompressor.getCrc32(), streamCompressor.getBytesWrittenForLastEntry(),
183 streamCompressor.getBytesRead()));
184 }
185
186
187
188
189
190
191 @Override
192 public void close() throws IOException {
193 if (!isClosed.compareAndSet(false, true)) {
194 return;
195 }
196 try {
197 if (zipEntryWriter != null) {
198 zipEntryWriter.close();
199 }
200 backingStore.close();
201 } finally {
202 streamCompressor.close();
203 }
204 }
205
206
207
208
209
210
211
212
213 public void writeTo(final ZipArchiveOutputStream target) throws IOException {
214 backingStore.closeForWriting();
215 try (InputStream data = backingStore.getInputStream()) {
216 for (final CompressedEntry compressedEntry : items) {
217 try (BoundedInputStream rawStream = new BoundedInputStream(data, compressedEntry.compressedSize)) {
218 target.addRawArchiveEntry(compressedEntry.transferToArchiveEntry(), rawStream);
219 }
220 }
221 }
222 }
223
224
225
226
227
228
229
230 public ZipEntryWriter zipEntryWriter() throws IOException {
231 if (zipEntryWriter == null) {
232 zipEntryWriter = new ZipEntryWriter(this);
233 }
234 return zipEntryWriter;
235 }
236 }