Home | History | Annotate | Download | only in examples
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements.  See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership.  The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the
      7  * "License"); you may not use this file except in compliance
      8  * with the License.  You may obtain a copy of the License at
      9  *
     10  * http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing,
     13  * software distributed under the License is distributed on an
     14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15  * KIND, either express or implied.  See the License for the
     16  * specific language governing permissions and limitations
     17  * under the License.
     18  */
     19 package org.apache.commons.compress.archivers.examples;
     20 
     21 import java.io.BufferedInputStream;
     22 import java.io.File;
     23 import java.io.IOException;
     24 import java.io.InputStream;
     25 import java.io.OutputStream;
     26 import java.nio.channels.Channels;
     27 import java.nio.channels.FileChannel;
     28 import java.nio.channels.SeekableByteChannel;
     29 import java.nio.file.Files;
     30 import java.nio.file.StandardOpenOption;
     31 
     32 import org.apache.commons.compress.archivers.ArchiveEntry;
     33 import org.apache.commons.compress.archivers.ArchiveException;
     34 import org.apache.commons.compress.archivers.ArchiveOutputStream;
     35 import org.apache.commons.compress.archivers.ArchiveStreamFactory;
     36 import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;
     37 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
     38 import org.apache.commons.compress.utils.IOUtils;
     39 
     40 /**
     41  * Provides a high level API for creating archives.
     42  * @since 1.17
     43  */
     44 public class Archiver {
     45 
     46     private interface ArchiveEntryCreator {
     47         ArchiveEntry create(File f, String entryName) throws IOException;
     48     }
     49 
     50     private interface ArchiveEntryConsumer {
     51         void accept(File source, ArchiveEntry entry) throws IOException;
     52     }
     53 
     54     private interface Finisher {
     55         void finish() throws IOException;
     56     }
     57 
     58     /**
     59      * Creates an archive {@code target} using the format {@code
     60      * format} by recursively including all files and directories in
     61      * {@code directory}.
     62      *
     63      * @param format the archive format. This uses the same format as
     64      * accepted by {@link ArchiveStreamFactory}.
     65      * @param target the file to write the new archive to.
     66      * @param directory the directory that contains the files to archive.
     67      * @throws IOException if an I/O error occurs
     68      * @throws ArchiveException if the archive cannot be created for other reasons
     69      */
     70     public void create(String format, File target, File directory) throws IOException, ArchiveException {
     71         if (prefersSeekableByteChannel(format)) {
     72             try (SeekableByteChannel c = FileChannel.open(target.toPath(), StandardOpenOption.WRITE,
     73                 StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
     74                 create(format, c, directory);
     75             }
     76             return;
     77         }
     78         try (OutputStream o = Files.newOutputStream(target.toPath())) {
     79             create(format, o, directory);
     80         }
     81     }
     82 
     83     /**
     84      * Creates an archive {@code target} using the format {@code
     85      * format} by recursively including all files and directories in
     86      * {@code directory}.
     87      *
     88      * @param format the archive format. This uses the same format as
     89      * accepted by {@link ArchiveStreamFactory}.
     90      * @param target the stream to write the new archive to.
     91      * @param directory the directory that contains the files to archive.
     92      * @throws IOException if an I/O error occurs
     93      * @throws ArchiveException if the archive cannot be created for other reasons
     94      */
     95     public void create(String format, OutputStream target, File directory) throws IOException, ArchiveException {
     96         create(new ArchiveStreamFactory().createArchiveOutputStream(format, target), directory);
     97     }
     98 
     99     /**
    100      * Creates an archive {@code target} using the format {@code
    101      * format} by recursively including all files and directories in
    102      * {@code directory}.
    103      *
    104      * @param format the archive format. This uses the same format as
    105      * accepted by {@link ArchiveStreamFactory}.
    106      * @param target the channel to write the new archive to.
    107      * @param directory the directory that contains the files to archive.
    108      * @throws IOException if an I/O error occurs
    109      * @throws ArchiveException if the archive cannot be created for other reasons
    110      */
    111     public void create(String format, SeekableByteChannel target, File directory)
    112         throws IOException, ArchiveException {
    113         if (!prefersSeekableByteChannel(format)) {
    114             create(format, Channels.newOutputStream(target), directory);
    115         } else if (ArchiveStreamFactory.ZIP.equalsIgnoreCase(format)) {
    116             create(new ZipArchiveOutputStream(target), directory);
    117         } else if (ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format)) {
    118             create(new SevenZOutputFile(target), directory);
    119         } else {
    120             // never reached as prefersSeekableByteChannel only returns true for ZIP and 7z
    121             throw new ArchiveException("don't know how to handle format " + format);
    122         }
    123     }
    124 
    125     /**
    126      * Creates an archive {@code target} by recursively including all
    127      * files and directories in {@code directory}.
    128      *
    129      * @param target the stream to write the new archive to.
    130      * @param directory the directory that contains the files to archive.
    131      * @throws IOException if an I/O error occurs
    132      * @throws ArchiveException if the archive cannot be created for other reasons
    133      */
    134     public void create(final ArchiveOutputStream target, File directory)
    135         throws IOException, ArchiveException {
    136         create(directory, new ArchiveEntryCreator() {
    137             public ArchiveEntry create(File f, String entryName) throws IOException {
    138                 return target.createArchiveEntry(f, entryName);
    139             }
    140         }, new ArchiveEntryConsumer() {
    141             public void accept(File source, ArchiveEntry e) throws IOException {
    142                 target.putArchiveEntry(e);
    143                 if (!e.isDirectory()) {
    144                     try (InputStream in = new BufferedInputStream(Files.newInputStream(source.toPath()))) {
    145                         IOUtils.copy(in, target);
    146                     }
    147                 }
    148                 target.closeArchiveEntry();
    149             }
    150         }, new Finisher() {
    151             public void finish() throws IOException {
    152                 target.finish();
    153             }
    154         });
    155     }
    156 
    157     /**
    158      * Creates an archive {@code target} by recursively including all
    159      * files and directories in {@code directory}.
    160      *
    161      * @param target the file to write the new archive to.
    162      * @param directory the directory that contains the files to archive.
    163      * @throws IOException if an I/O error occurs
    164      */
    165     public void create(final SevenZOutputFile target, File directory) throws IOException {
    166         create(directory, new ArchiveEntryCreator() {
    167             public ArchiveEntry create(File f, String entryName) throws IOException {
    168                 return target.createArchiveEntry(f, entryName);
    169             }
    170         }, new ArchiveEntryConsumer() {
    171             public void accept(File source, ArchiveEntry e) throws IOException {
    172                 target.putArchiveEntry(e);
    173                 if (!e.isDirectory()) {
    174                     final byte[] buffer = new byte[8024];
    175                     int n = 0;
    176                     long count = 0;
    177                     try (InputStream in = new BufferedInputStream(Files.newInputStream(source.toPath()))) {
    178                         while (-1 != (n = in.read(buffer))) {
    179                             target.write(buffer, 0, n);
    180                             count += n;
    181                         }
    182                     }
    183                 }
    184                 target.closeArchiveEntry();
    185             }
    186         }, new Finisher() {
    187             public void finish() throws IOException {
    188                 target.finish();
    189             }
    190         });
    191     }
    192 
    193     private boolean prefersSeekableByteChannel(String format) {
    194         return ArchiveStreamFactory.ZIP.equalsIgnoreCase(format) || ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format);
    195     }
    196 
    197     private void create(File directory, ArchiveEntryCreator creator, ArchiveEntryConsumer consumer,
    198         Finisher finisher) throws IOException {
    199         create("", directory, creator, consumer);
    200         finisher.finish();
    201     }
    202 
    203     private void create(String prefix, File directory, ArchiveEntryCreator creator, ArchiveEntryConsumer consumer)
    204         throws IOException {
    205         File[] children = directory.listFiles();
    206         if (children == null) {
    207             return;
    208         }
    209         for (File f : children) {
    210             String entryName = prefix + f.getName() + (f.isDirectory() ? "/" : "");
    211             consumer.accept(f, creator.create(f, entryName));
    212             if (f.isDirectory()) {
    213                 create(entryName, f, creator, consumer);
    214             }
    215         }
    216     }
    217 }
    218