📄 zipoutputstream.java
字号:
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */package org.apache.tools.zip;import java.io.File;import java.io.FileOutputStream;import java.io.FilterOutputStream;import java.io.IOException;import java.io.OutputStream;import java.io.RandomAccessFile;import java.io.UnsupportedEncodingException;import java.util.Date;import java.util.Hashtable;import java.util.Vector;import java.util.zip.CRC32;import java.util.zip.Deflater;import java.util.zip.ZipException;/** * Reimplementation of {@link java.util.zip.ZipOutputStream * java.util.zip.ZipOutputStream} that does handle the extended * functionality of this package, especially internal/external file * attributes and extra fields with different layouts for local file * data and central directory entries. * * <p>This class will try to use {@link java.io.RandomAccessFile * RandomAccessFile} when you know that the output is going to go to a * file.</p> * * <p>If RandomAccessFile cannot be used, this implementation will use * a Data Descriptor to store size and CRC information for {@link * #DEFLATED DEFLATED} entries, this means, you don't need to * calculate them yourself. Unfortunately this is not possible for * the {@link #STORED STORED} method, here setting the CRC and * uncompressed size information is required before {@link * #putNextEntry putNextEntry} can be called.</p> * */public class ZipOutputStream extends FilterOutputStream { private static final int BYTE_MASK = 0xFF; private static final int SHORT = 2; private static final int WORD = 4; private static final int BUFFER_SIZE = 512; /** * Compression method for deflated entries. * * @since 1.1 */ public static final int DEFLATED = java.util.zip.ZipEntry.DEFLATED; /** * Default compression level for deflated entries. * * @since Ant 1.7 */ public static final int DEFAULT_COMPRESSION = Deflater.DEFAULT_COMPRESSION; /** * Compression method for stored entries. * * @since 1.1 */ public static final int STORED = java.util.zip.ZipEntry.STORED; /** * Current entry. * * @since 1.1 */ private ZipEntry entry; /** * The file comment. * * @since 1.1 */ private String comment = ""; /** * Compression level for next entry. * * @since 1.1 */ private int level = DEFAULT_COMPRESSION; /** * Has the compression level changed when compared to the last * entry? * * @since 1.5 */ private boolean hasCompressionLevelChanged = false; /** * Default compression method for next entry. * * @since 1.1 */ private int method = java.util.zip.ZipEntry.DEFLATED; /** * List of ZipEntries written so far. * * @since 1.1 */ private Vector entries = new Vector(); /** * CRC instance to avoid parsing DEFLATED data twice. * * @since 1.1 */ private CRC32 crc = new CRC32(); /** * Count the bytes written to out. * * @since 1.1 */ private long written = 0; /** * Data for local header data * * @since 1.1 */ private long dataStart = 0; /** * Offset for CRC entry in the local file header data for the * current entry starts here. * * @since 1.15 */ private long localDataStart = 0; /** * Start of central directory. * * @since 1.1 */ private long cdOffset = 0; /** * Length of central directory. * * @since 1.1 */ private long cdLength = 0; /** * Helper, a 0 as ZipShort. * * @since 1.1 */ private static final byte[] ZERO = {0, 0}; /** * Helper, a 0 as ZipLong. * * @since 1.1 */ private static final byte[] LZERO = {0, 0, 0, 0}; /** * Holds the offsets of the LFH starts for each entry. * * @since 1.1 */ private Hashtable offsets = new Hashtable(); /** * The encoding to use for filenames and the file comment. * * <p>For a list of possible values see <a * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>. * Defaults to the platform's default character encoding.</p> * * @since 1.3 */ private String encoding = null; // CheckStyle:VisibilityModifier OFF - bc /** * This Deflater object is used for output. * * <p>This attribute is only protected to provide a level of API * backwards compatibility. This class used to extend {@link * java.util.zip.DeflaterOutputStream DeflaterOutputStream} up to * Revision 1.13.</p> * * @since 1.14 */ protected Deflater def = new Deflater(level, true); /** * This buffer servers as a Deflater. * * <p>This attribute is only protected to provide a level of API * backwards compatibility. This class used to extend {@link * java.util.zip.DeflaterOutputStream DeflaterOutputStream} up to * Revision 1.13.</p> * * @since 1.14 */ protected byte[] buf = new byte[BUFFER_SIZE]; // CheckStyle:VisibilityModifier ON /** * Optional random access output. * * @since 1.14 */ private RandomAccessFile raf = null; /** * Creates a new ZIP OutputStream filtering the underlying stream. * @param out the outputstream to zip * @since 1.1 */ public ZipOutputStream(OutputStream out) { super(out); } /** * Creates a new ZIP OutputStream writing to a File. Will use * random access if possible. * @param file the file to zip to * @since 1.14 * @throws IOException on error */ public ZipOutputStream(File file) throws IOException { super(null); try { raf = new RandomAccessFile(file, "rw"); raf.setLength(0); } catch (IOException e) { if (raf != null) { try { raf.close(); } catch (IOException inner) { // ignore } raf = null; } out = new FileOutputStream(file); } } /** * This method indicates whether this archive is writing to a seekable stream (i.e., to a random * access file). * * <p>For seekable streams, you don't need to calculate the CRC or * uncompressed size for {@link #STORED} entries before * invoking {@link #putNextEntry}. * @return true if seekable * @since 1.17 */ public boolean isSeekable() { return raf != null; } /** * The encoding to use for filenames and the file comment. * * <p>For a list of possible values see <a * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>. * Defaults to the platform's default character encoding.</p> * @param encoding the encoding value * @since 1.3 */ public void setEncoding(String encoding) { this.encoding = encoding; } /** * The encoding to use for filenames and the file comment. * * @return null if using the platform's default character encoding. * * @since 1.3 */ public String getEncoding() { return encoding; } /** * Finishs writing the contents and closes this as well as the * underlying stream. * * @since 1.1 * @throws IOException on error */ public void finish() throws IOException { closeEntry(); cdOffset = written; for (int i = 0, entriesSize = entries.size(); i < entriesSize; i++) { writeCentralFileHeader((ZipEntry) entries.elementAt(i)); } cdLength = written - cdOffset; writeCentralDirectoryEnd(); offsets.clear(); entries.removeAllElements(); } /** * Writes all necessary data for this entry. * * @since 1.1 * @throws IOException on error */ public void closeEntry() throws IOException { if (entry == null) { return; } long realCrc = crc.getValue(); crc.reset(); if (entry.getMethod() == DEFLATED) { def.finish(); while (!def.finished()) { deflate(); } entry.setSize(adjustToLong(def.getTotalIn())); entry.setCompressedSize(adjustToLong(def.getTotalOut())); entry.setCrc(realCrc); def.reset(); written += entry.getCompressedSize(); } else if (raf == null) { if (entry.getCrc() != realCrc) { throw new ZipException("bad CRC checksum for entry " + entry.getName() + ": " + Long.toHexString(entry.getCrc()) + " instead of " + Long.toHexString(realCrc)); } if (entry.getSize() != written - dataStart) { throw new ZipException("bad size for entry " + entry.getName() + ": " + entry.getSize() + " instead of " + (written - dataStart)); } } else { /* method is STORED and we used RandomAccessFile */ long size = written - dataStart; entry.setSize(size); entry.setCompressedSize(size); entry.setCrc(realCrc); } // If random access output, write the local file header containing // the correct CRC and compressed/uncompressed sizes if (raf != null) { long save = raf.getFilePointer(); raf.seek(localDataStart); writeOut(ZipLong.getBytes(entry.getCrc())); writeOut(ZipLong.getBytes(entry.getCompressedSize())); writeOut(ZipLong.getBytes(entry.getSize())); raf.seek(save); } writeDataDescriptor(entry); entry = null; } /** * Begin writing next entry. * @param ze the entry to write * @since 1.1 * @throws IOException on error */ public void putNextEntry(ZipEntry ze) throws IOException { closeEntry(); entry = ze; entries.addElement(entry); if (entry.getMethod() == -1) { // not specified entry.setMethod(method); } if (entry.getTime() == -1) { // not specified entry.setTime(System.currentTimeMillis()); } // Size/CRC not required if RandomAccessFile is used if (entry.getMethod() == STORED && raf == null) { if (entry.getSize() == -1) { throw new ZipException("uncompressed size is required for" + " STORED method when not writing to a" + " file"); } if (entry.getCrc() == -1) { throw new ZipException("crc checksum is required for STORED" + " method when not writing to a file"); } entry.setCompressedSize(entry.getSize()); } if (entry.getMethod() == DEFLATED && hasCompressionLevelChanged) { def.setLevel(level); hasCompressionLevelChanged = false; } writeLocalFileHeader(entry); } /** * Set the file comment. * @param comment the comment * @since 1.1 */ public void setComment(String comment) { this.comment = comment; } /** * Sets the compression level for subsequent entries. * * <p>Default is Deflater.DEFAULT_COMPRESSION.</p> * @param level the compression level. * @throws IllegalArgumentException if an invalid compression level is specified. * @since 1.1 */ public void setLevel(int level) { if (level < Deflater.DEFAULT_COMPRESSION || level > Deflater.BEST_COMPRESSION) { throw new IllegalArgumentException( "Invalid compression level: " + level); } hasCompressionLevelChanged = (this.level != level); this.level = level;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -