1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.archivers.examples;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.OutputStream;
24 import java.nio.channels.Channels;
25 import java.nio.channels.FileChannel;
26 import java.nio.channels.SeekableByteChannel;
27 import java.nio.file.FileVisitOption;
28 import java.nio.file.FileVisitResult;
29 import java.nio.file.Files;
30 import java.nio.file.LinkOption;
31 import java.nio.file.Path;
32 import java.nio.file.SimpleFileVisitor;
33 import java.nio.file.StandardOpenOption;
34 import java.nio.file.attribute.BasicFileAttributes;
35 import java.util.EnumSet;
36 import java.util.Objects;
37
38 import org.apache.commons.compress.archivers.ArchiveEntry;
39 import org.apache.commons.compress.archivers.ArchiveException;
40 import org.apache.commons.compress.archivers.ArchiveOutputStream;
41 import org.apache.commons.compress.archivers.ArchiveStreamFactory;
42 import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
43 import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;
44 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
45 import org.apache.commons.compress.utils.IOUtils;
46
47
48
49
50
51
52
53 public class Archiver {
54
55 private static class ArchiverFileVisitor<O extends ArchiveOutputStream<E>, E extends ArchiveEntry> extends SimpleFileVisitor<Path> {
56
57 private final O outputStream;
58 private final Path directory;
59 private final LinkOption[] linkOptions;
60
61 private ArchiverFileVisitor(final O target, final Path directory, final LinkOption... linkOptions) {
62 this.outputStream = target;
63 this.directory = directory;
64 this.linkOptions = linkOptions == null ? IOUtils.EMPTY_LINK_OPTIONS : linkOptions.clone();
65 }
66
67 @Override
68 public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
69 return visit(dir, attrs, false);
70 }
71
72 protected FileVisitResult visit(final Path path, final BasicFileAttributes attrs, final boolean isFile) throws IOException {
73 Objects.requireNonNull(path);
74 Objects.requireNonNull(attrs);
75 final String name = directory.relativize(path).toString().replace('\\', '/');
76 if (!name.isEmpty()) {
77 final E archiveEntry = outputStream.createArchiveEntry(path, isFile || name.endsWith("/") ? name : name + "/", linkOptions);
78 outputStream.putArchiveEntry(archiveEntry);
79 if (isFile) {
80
81 outputStream.write(path);
82 }
83 outputStream.closeArchiveEntry();
84 }
85 return FileVisitResult.CONTINUE;
86 }
87
88 @Override
89 public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
90 return visit(file, attrs, true);
91 }
92 }
93
94
95
96
97 public static final EnumSet<FileVisitOption> EMPTY_FileVisitOption = EnumSet.noneOf(FileVisitOption.class);
98
99
100
101
102 public Archiver() {
103
104 }
105
106
107
108
109
110
111
112
113 public void create(final ArchiveOutputStream<?> target, final File directory) throws IOException {
114 create(target, directory.toPath(), EMPTY_FileVisitOption);
115 }
116
117
118
119
120
121
122
123
124
125 public void create(final ArchiveOutputStream<?> target, final Path directory) throws IOException {
126 create(target, directory, EMPTY_FileVisitOption);
127 }
128
129
130
131
132
133
134
135
136
137
138
139 public void create(final ArchiveOutputStream<?> target, final Path directory, final EnumSet<FileVisitOption> fileVisitOptions,
140 final LinkOption... linkOptions) throws IOException {
141 Files.walkFileTree(directory, fileVisitOptions, Integer.MAX_VALUE, new ArchiverFileVisitor<>(target, directory, linkOptions));
142 target.finish();
143 }
144
145
146
147
148
149
150
151
152 public void create(final SevenZOutputFile target, final File directory) throws IOException {
153 create(target, directory.toPath());
154 }
155
156
157
158
159
160
161
162
163
164 public void create(final SevenZOutputFile target, final Path directory) throws IOException {
165
166 Files.walkFileTree(directory, new ArchiverFileVisitor<ArchiveOutputStream<ArchiveEntry>, ArchiveEntry>(null, directory) {
167
168 @Override
169 protected FileVisitResult visit(final Path path, final BasicFileAttributes attrs, final boolean isFile) throws IOException {
170 Objects.requireNonNull(path);
171 Objects.requireNonNull(attrs);
172 final String name = directory.relativize(path).toString().replace('\\', '/');
173 if (!name.isEmpty()) {
174 final SevenZArchiveEntry archiveEntry = target.createArchiveEntry(path, isFile || name.endsWith("/") ? name : name + "/");
175 target.putArchiveEntry(archiveEntry);
176 if (isFile) {
177
178 target.write(path);
179 }
180 target.closeArchiveEntry();
181 }
182 return FileVisitResult.CONTINUE;
183 }
184
185 });
186 target.finish();
187 }
188
189
190
191
192
193
194
195
196
197
198
199 public void create(final String format, final File target, final File directory) throws IOException, ArchiveException {
200 create(format, target.toPath(), directory.toPath());
201 }
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 @Deprecated
220 public void create(final String format, final OutputStream target, final File directory) throws IOException, ArchiveException {
221 create(format, target, directory, CloseableConsumer.NULL_CONSUMER);
222 }
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 public void create(final String format, final OutputStream target, final File directory, final CloseableConsumer closeableConsumer)
243 throws IOException, ArchiveException {
244 try (CloseableConsumerAdapter c = new CloseableConsumerAdapter(closeableConsumer)) {
245 final ArchiveOutputStream<? extends ArchiveEntry> archiveOutputStream = ArchiveStreamFactory.DEFAULT.createArchiveOutputStream(format, target);
246 create(c.track(archiveOutputStream), directory);
247 }
248 }
249
250
251
252
253
254
255
256
257
258
259
260
261 public void create(final String format, final Path target, final Path directory) throws IOException, ArchiveException {
262 if (prefersSeekableByteChannel(format)) {
263 try (SeekableByteChannel channel = FileChannel.open(target, StandardOpenOption.WRITE, StandardOpenOption.CREATE,
264 StandardOpenOption.TRUNCATE_EXISTING)) {
265 create(format, channel, directory);
266 return;
267 }
268 }
269 try (@SuppressWarnings("resource")
270 ArchiveOutputStream<?> outputStream = ArchiveStreamFactory.DEFAULT.createArchiveOutputStream(format, Files.newOutputStream(target))) {
271 create(outputStream, directory, EMPTY_FileVisitOption);
272 }
273 }
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291 @Deprecated
292 public void create(final String format, final SeekableByteChannel target, final File directory) throws IOException, ArchiveException {
293 create(format, target, directory, CloseableConsumer.NULL_CONSUMER);
294 }
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314 public void create(final String format, final SeekableByteChannel target, final File directory, final CloseableConsumer closeableConsumer)
315 throws IOException, ArchiveException {
316 try (CloseableConsumerAdapter c = new CloseableConsumerAdapter(closeableConsumer)) {
317 if (!prefersSeekableByteChannel(format)) {
318 create(format, c.track(Channels.newOutputStream(target)), directory);
319 } else if (ArchiveStreamFactory.ZIP.equalsIgnoreCase(format)) {
320 create(c.track(new ZipArchiveOutputStream(target)), directory);
321 } else if (ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format)) {
322 create(c.track(new SevenZOutputFile(target)), directory);
323 } else {
324
325 throw new ArchiveException("Don't know how to handle format " + format);
326 }
327 }
328 }
329
330
331
332
333
334
335
336
337
338
339
340 public void create(final String format, final SeekableByteChannel target, final Path directory) throws IOException {
341 if (ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format)) {
342 try (SevenZOutputFile sevenZFile = new SevenZOutputFile(target)) {
343 create(sevenZFile, directory);
344 }
345 } else if (ArchiveStreamFactory.ZIP.equalsIgnoreCase(format)) {
346 try (ZipArchiveOutputStream archiveOutputStream = new ZipArchiveOutputStream(target)) {
347 create(archiveOutputStream, directory, EMPTY_FileVisitOption);
348 }
349 } else {
350 throw new IllegalStateException(format);
351 }
352 }
353
354 private boolean prefersSeekableByteChannel(final String format) {
355 return ArchiveStreamFactory.ZIP.equalsIgnoreCase(format) || ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format);
356 }
357 }