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 }