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 target;
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.target = 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 = target.createArchiveEntry(path, isFile || name.endsWith("/") ? name : name + "/", linkOptions);
78 target.putArchiveEntry(archiveEntry);
79 if (isFile) {
80
81 Files.copy(path, target);
82 }
83 target.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
103
104
105
106 public void create(final ArchiveOutputStream<?> target, final File directory) throws IOException {
107 create(target, directory.toPath(), EMPTY_FileVisitOption);
108 }
109
110
111
112
113
114
115
116
117
118 public void create(final ArchiveOutputStream<?> target, final Path directory) throws IOException {
119 create(target, directory, EMPTY_FileVisitOption);
120 }
121
122
123
124
125
126
127
128
129
130
131
132 public void create(final ArchiveOutputStream<?> target, final Path directory, final EnumSet<FileVisitOption> fileVisitOptions,
133 final LinkOption... linkOptions) throws IOException {
134 Files.walkFileTree(directory, fileVisitOptions, Integer.MAX_VALUE, new ArchiverFileVisitor<>(target, directory, linkOptions));
135 target.finish();
136 }
137
138
139
140
141
142
143
144
145 public void create(final SevenZOutputFile target, final File directory) throws IOException {
146 create(target, directory.toPath());
147 }
148
149
150
151
152
153
154
155
156
157 public void create(final SevenZOutputFile target, final Path directory) throws IOException {
158
159 Files.walkFileTree(directory, new ArchiverFileVisitor<ArchiveOutputStream<ArchiveEntry>, ArchiveEntry>(null, directory) {
160
161 @Override
162 protected FileVisitResult visit(final Path path, final BasicFileAttributes attrs, final boolean isFile) throws IOException {
163 Objects.requireNonNull(path);
164 Objects.requireNonNull(attrs);
165 final String name = directory.relativize(path).toString().replace('\\', '/');
166 if (!name.isEmpty()) {
167 final SevenZArchiveEntry archiveEntry = target.createArchiveEntry(path, isFile || name.endsWith("/") ? name : name + "/");
168 target.putArchiveEntry(archiveEntry);
169 if (isFile) {
170
171 target.write(path);
172 }
173 target.closeArchiveEntry();
174 }
175 return FileVisitResult.CONTINUE;
176 }
177
178 });
179 target.finish();
180 }
181
182
183
184
185
186
187
188
189
190
191
192 public void create(final String format, final File target, final File directory) throws IOException, ArchiveException {
193 create(format, target.toPath(), directory.toPath());
194 }
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212 @Deprecated
213 public void create(final String format, final OutputStream target, final File directory) throws IOException, ArchiveException {
214 create(format, target, directory, CloseableConsumer.NULL_CONSUMER);
215 }
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235 public void create(final String format, final OutputStream target, final File directory, final CloseableConsumer closeableConsumer)
236 throws IOException, ArchiveException {
237 try (CloseableConsumerAdapter c = new CloseableConsumerAdapter(closeableConsumer)) {
238 final ArchiveOutputStream<? extends ArchiveEntry> archiveOutputStream = ArchiveStreamFactory.DEFAULT.createArchiveOutputStream(format, target);
239 create(c.track(archiveOutputStream), directory);
240 }
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254 public void create(final String format, final Path target, final Path directory) throws IOException, ArchiveException {
255 if (prefersSeekableByteChannel(format)) {
256 try (SeekableByteChannel channel = FileChannel.open(target, StandardOpenOption.WRITE, StandardOpenOption.CREATE,
257 StandardOpenOption.TRUNCATE_EXISTING)) {
258 create(format, channel, directory);
259 return;
260 }
261 }
262 try (@SuppressWarnings("resource")
263 ArchiveOutputStream<?> outputStream = ArchiveStreamFactory.DEFAULT.createArchiveOutputStream(format, Files.newOutputStream(target))) {
264 create(outputStream, directory, EMPTY_FileVisitOption);
265 }
266 }
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284 @Deprecated
285 public void create(final String format, final SeekableByteChannel target, final File directory) throws IOException, ArchiveException {
286 create(format, target, directory, CloseableConsumer.NULL_CONSUMER);
287 }
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307 public void create(final String format, final SeekableByteChannel target, final File directory, final CloseableConsumer closeableConsumer)
308 throws IOException, ArchiveException {
309 try (CloseableConsumerAdapter c = new CloseableConsumerAdapter(closeableConsumer)) {
310 if (!prefersSeekableByteChannel(format)) {
311 create(format, c.track(Channels.newOutputStream(target)), directory);
312 } else if (ArchiveStreamFactory.ZIP.equalsIgnoreCase(format)) {
313 create(c.track(new ZipArchiveOutputStream(target)), directory);
314 } else if (ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format)) {
315 create(c.track(new SevenZOutputFile(target)), directory);
316 } else {
317
318 throw new ArchiveException("Don't know how to handle format " + format);
319 }
320 }
321 }
322
323
324
325
326
327
328
329
330
331
332
333 public void create(final String format, final SeekableByteChannel target, final Path directory) throws IOException {
334 if (ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format)) {
335 try (SevenZOutputFile sevenZFile = new SevenZOutputFile(target)) {
336 create(sevenZFile, directory);
337 }
338 } else if (ArchiveStreamFactory.ZIP.equalsIgnoreCase(format)) {
339 try (ZipArchiveOutputStream archiveOutputStream = new ZipArchiveOutputStream(target)) {
340 create(archiveOutputStream, directory, EMPTY_FileVisitOption);
341 }
342 } else {
343 throw new IllegalStateException(format);
344 }
345 }
346
347 private boolean prefersSeekableByteChannel(final String format) {
348 return ArchiveStreamFactory.ZIP.equalsIgnoreCase(format) || ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format);
349 }
350 }