View Javadoc
1   /*
2    * Copyright (C) 2005-2015 Schlichtherle IT Services.
3    * All rights reserved. Use is subject to license terms.
4    */
5   package net.java.truevfs.comp.zip;
6   
7   import edu.umd.cs.findbugs.annotations.CreatesObligation;
8   import java.io.IOException;
9   import java.io.OutputStream;
10  import java.nio.charset.Charset;
11  import java.util.ArrayList;
12  import java.util.Iterator;
13  import java.util.List;
14  import java.util.Objects;
15  import javax.annotation.CheckForNull;
16  import javax.annotation.Nullable;
17  import javax.annotation.WillCloseWhenClosed;
18  import javax.annotation.concurrent.NotThreadSafe;
19  import net.java.truecommons.io.OneTimeSink;
20  
21  /**
22   * Replacement for
23   * {@link java.util.zip.ZipOutputStream java.util.zip.ZipOutputStream}.
24   * <p>
25   * This class starts writing ordinary ZIP32 File Format.
26   * It automatically adds ZIP64 extensions if required,
27   * i.e. if the file size exceeds 4GB or more than 65535 entries are written.
28   * This implies that the class may produce ZIP archive files which cannot
29   * be read by older ZIP implementations.
30   * <p>
31   * If the system property {@code net.truevfs.kernel.io.zip.zip64ext}
32   * is set to {@code true} (case is ignored),
33   * then ZIP64 extensions are always added when writing a ZIP archive file,
34   * regardless of its size.
35   * This system property is primarily intended for unit testing purposes.
36   * During normal operations, it should not be set as many
37   * third party tools would not treat the redundant ZIP64 extensions
38   * correctly.
39   * Note that it's impossible to inhibit ZIP64 extensions.
40   *
41   * @see     ZipFile
42   * @author  Christian Schlichtherle
43   */
44  @NotThreadSafe
45  public class ZipOutputStream extends AbstractZipOutputStream<ZipEntry> {
46  
47      /**
48       * The default character set used for entry names and comments in ZIP files.
49       * This is {@code "UTF-8"} for compatibility with Sun's JDK implementation.
50       */
51      public static final Charset DEFAULT_CHARSET = Constants.DEFAULT_CHARSET;
52  
53      private static final ZipOutputStreamParameters DEFAULT_PARAM
54              = new DefaultZipOutputStreamParameters(DEFAULT_CHARSET);
55  
56      private @CheckForNull ZipCryptoParameters cryptoParameters;
57  
58      /**
59       * Constructs a ZIP output stream which decorates the given output stream
60       * using the {@code "UTF-8"} charset.
61       *
62       * @param  out The output stream to write the ZIP file to.
63       * @throws IOException on any I/O error.
64       */
65      @CreatesObligation
66      public ZipOutputStream(@WillCloseWhenClosed OutputStream out)
67      throws IOException {
68          super(new OneTimeSink(out), null, DEFAULT_PARAM);
69      }
70  
71      /**
72       * Constructs a ZIP output stream which decorates the given output stream
73       * using the given charset.
74       *
75       * @param  out The output stream to write the ZIP file to.
76       * @param  charset the character set to use.
77       * @throws IOException on any I/O error.
78       */
79      @CreatesObligation
80      public ZipOutputStream(@WillCloseWhenClosed OutputStream out, Charset charset)
81      throws IOException {
82          super(  new OneTimeSink(out), null,
83                  new DefaultZipOutputStreamParameters(charset));
84      }
85  
86      /**
87       * Constructs a ZIP output stream which decorates the given output stream
88       * and appends to the given ZIP file.
89       *
90       * @param  out The output stream to write the ZIP file to.
91       *         If {@code appendee} is not {@code null}, then this must be set
92       *         up so that it appends to the same ZIP file from which
93       *         {@code appendee} is reading.
94       * @param  appendee the ZIP file to append to.
95       *         This may already be closed.
96       * @throws IOException on any I/O error.
97       */
98      @CreatesObligation
99      public ZipOutputStream(
100             @WillCloseWhenClosed OutputStream out,
101             ZipFile appendee)
102     throws IOException {
103         super(new OneTimeSink(out), Objects.requireNonNull(appendee), DEFAULT_PARAM);
104     }
105 
106     /**
107      * Returns a safe iteration of clones for all entries written to this ZIP
108      * file so far.
109      * This method takes a snapshot of the collection of all entries and
110      * clones them while iterating, so concurrent modifications or state
111      * changes do not affect this instance, the returned enumeration or the
112      * enumerated ZIP entries.
113      * The iteration does not support element removal.
114      */
115     @Override
116     public Iterator<ZipEntry> iterator() {
117         final class EntryIterator implements Iterator<ZipEntry> {
118             final Iterator<ZipEntry> i;
119 
120             EntryIterator() {
121                 List<ZipEntry> l = new ArrayList<>(ZipOutputStream.super.size());
122                 Iterator<ZipEntry> si = ZipOutputStream.super.iterator();
123                 while (si.hasNext())
124                     l.add(si.next());
125                 i = l.iterator();
126             }
127 
128             @Override
129             public boolean hasNext() {
130                 return i.hasNext();
131             }
132 
133             @Override
134             public ZipEntry next() {
135                 return i.next().clone();
136             }
137 
138             @Override
139             public void remove() {
140                 throw new UnsupportedOperationException();
141             }
142         } // class EntryIterator
143 
144         return new EntryIterator();
145     }
146 
147     /**
148      * Returns a clone of the entry for the given {@code name} or {@code null}
149      * if no entry with this name exists in this ZIP file.
150      *
151      * @param  name the name of the ZIP entry.
152      * @return A clone of the entry for the given {@code name} or {@code null}
153      *         if no entry with this name exists in this ZIP file.
154      */
155     @Override
156     public ZipEntry entry(String name) {
157         final ZipEntry entry = super.entry(name);
158         return entry != null ? entry.clone() : null;
159     }
160 
161     @Override
162     public @Nullable ZipCryptoParameters getCryptoParameters() {
163         return cryptoParameters;
164     }
165 
166     /**
167      * Sets the parameters for encryption or authentication of entries.
168      * <p>
169      * Note that only {@link WinZipAesParameters WinZip AES encryption} is
170      * currently supported.
171      *
172      * @param cryptoParameters the parameters for encryption or authentication
173      *        of entries.
174      */
175     public void setCryptoParameters(
176             final @CheckForNull ZipCryptoParameters cryptoParameters) {
177         this.cryptoParameters = cryptoParameters;
178     }
179 }