add zipOutputStream and change DeflaterOutputStream

This commit is contained in:
Aaron Davis 2013-07-08 14:13:08 -06:00
parent 6970bb26ae
commit 54484bc2eb
2 changed files with 151 additions and 105 deletions

View File

@ -11,16 +11,16 @@
package java.util.zip; package java.util.zip;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.FilterOutputStream;
import java.io.IOException; import java.io.IOException;
public class DeflaterOutputStream extends OutputStream { public class DeflaterOutputStream extends FilterOutputStream {
private final OutputStream out; protected final Deflater deflater;
private final Deflater deflater; protected final byte[] buffer;
private final byte[] buffer;
public DeflaterOutputStream(OutputStream out, Deflater deflater, int bufferSize) public DeflaterOutputStream(OutputStream out, Deflater deflater, int bufferSize)
{ {
this.out = out; super(out);
this.deflater = deflater; this.deflater = deflater;
this.buffer = new byte[bufferSize]; this.buffer = new byte[bufferSize];
} }

View File

@ -1,3 +1,13 @@
/* Copyright (c) 2008-2013, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.util.zip; package java.util.zip;
import java.io.IOException; import java.io.IOException;
@ -8,7 +18,6 @@ import java.util.List;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
import java.util.zip.Deflater; import java.util.zip.Deflater;
import java.io.BufferedOutputStream;
/** /**
@ -16,8 +25,12 @@ import java.io.BufferedOutputStream;
* Compression method defaults to DEFLATE * Compression method defaults to DEFLATE
* All hardcoded defaults match the defaults for openJDK, * All hardcoded defaults match the defaults for openJDK,
* including PKZip version, bit flags set, compression level, etc * including PKZip version, bit flags set, compression level, etc
*
* @author David Chau
* @author Aaron Davis
* @author Christopher Jordan
* @author Riley Moses
* *
* @author ReadyTalk Summer 2013 Intern Team
*/ */
public class ZipOutputStream extends DeflaterOutputStream { public class ZipOutputStream extends DeflaterOutputStream {
private static final int SIGNATURE = 0x04034b50; private static final int SIGNATURE = 0x04034b50;
@ -28,30 +41,25 @@ public class ZipOutputStream extends DeflaterOutputStream {
private static final int DATA_DESCRIPTER_HEADER = 0x08074b50; private static final int DATA_DESCRIPTER_HEADER = 0x08074b50;
private static final int END_OF_CENTRAL_DIRECTORY_SIG = 0x06054b50; private static final int END_OF_CENTRAL_DIRECTORY_SIG = 0x06054b50;
private static final int DEFAULT_LEVEL = 6; private static final int DEFAULT_LEVEL = 6;
private static final int INPUT_BUFFER_SIZE = 1024;
private final OutputStream out;
private DeflaterOutputStream deflaterStream;
private Deflater deflater;
private List<ZipEntry> entries; private List<ZipEntry> entries;
private CRC32 crc = new CRC32(); private CRC32 crc = new CRC32();
private ZipEntry currentEntry; private ZipEntry currentEntry; // holder for current entry
private int bytesWritten; // a counter for total bytes written
private int bytesWritten; private int sizeOfCentralDirectory; // a counter for central dir size
private int sizeOfCentralDirectory;
private byte[] buffer;
public ZipOutputStream(OutputStream outStream, int bufferSize) { // these are used for the function write(int b) to provide a speed increase
super(outStream); private byte[] inputBuffer = new byte[INPUT_BUFFER_SIZE];
out = outStream; private int bufferIndex;
public ZipOutputStream(OutputStream outStream) {
super(outStream, new Deflater(DEFAULT_LEVEL, true));
bytesWritten = 0; bytesWritten = 0;
sizeOfCentralDirectory = 0; sizeOfCentralDirectory = 0;
entries = new ArrayList<ZipEntry>(); entries = new ArrayList<ZipEntry>();
buffer = new byte[bufferSize];
deflater = new Deflater(DEFAULT_LEVEL, true);
}
public ZipOutputStream(OutputStream outStream) {
this(outStream, 4 * 1024);
} }
public void putNextEntry(ZipEntry e) throws IOException { public void putNextEntry(ZipEntry e) throws IOException {
@ -62,51 +70,24 @@ public class ZipOutputStream extends DeflaterOutputStream {
} }
public void closeEntry() throws IOException { public void closeEntry() throws IOException {
deflater.finish(); // write remainder of buffer if partially full
while (!deflater.finished()) { if (bufferIndex != 0) {
deflate(); write(inputBuffer, 0, bufferIndex);
bufferIndex = 0;
} }
deflater.dispose();
deflater.reset(); finish();
currentEntry.crc = (int) crc.getValue(); currentEntry.crc = (int) crc.getValue();
crc.reset(); crc.reset();
writeDataDescriptor(currentEntry); writeDataDescriptor(currentEntry);
} }
private void writeLocalHeader(ZipEntry e) throws IOException { @Override
writeFourBytes(SIGNATURE); // local header signature
writeTwoBytes(VERSION); // version used
writeTwoBytes(BITFLAG); // flags
writeTwoBytes(METHOD); // compression method
writeTwoBytes(e.modTime); // last modified time
writeTwoBytes(e.modDate); // last modified date
writeFourBytes(0); // CRC is 0 for local header
// with default flag settings, the compressed and uncompressed size
// is written here as 0 and written correctly in the data descripter
writeFourBytes(0); // compressed size
writeFourBytes(0); // uncompressed size
writeTwoBytes(e.name.length()); // length of file name
// extra field length, in this implementation extra field in not used
writeTwoBytes(0);
// write file name, return the number of bytes written
int len = writeVariableByteLength(e.getName());
bytesWritten += 30 + len;
}
private void writeDataDescriptor(ZipEntry currentEntry) throws IOException {
writeFourBytes(DATA_DESCRIPTER_HEADER); // data descripter header
writeFourBytes(currentEntry.crc); // crc value
writeFourBytes(currentEntry.compSize); // compressed size
writeFourBytes(currentEntry.uncompSize); // uncompressed size
bytesWritten += 16;
}
public void write(byte[] b, int offset, int length) throws IOException { public void write(byte[] b, int offset, int length) throws IOException {
if (offset < 0 || length < 0 || b.length - (offset + length) < 0)
throw new IndexOutOfBoundsException();
currentEntry.uncompSize += length; currentEntry.uncompSize += length;
crc.update(b, offset, length); crc.update(b, offset, length);
currentEntry.crc = (int) crc.getValue(); currentEntry.crc = (int) crc.getValue();
@ -116,10 +97,14 @@ public class ZipOutputStream extends DeflaterOutputStream {
deflate(); deflate();
} }
@Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
byte[] buf = new byte[1]; inputBuffer[bufferIndex] = (byte)(b & 0xff);
buf[0] = (byte)(b & 0xff); bufferIndex += 1;
write(buf, 0, 1); if (bufferIndex == 1024) {
write(inputBuffer, 0, bufferIndex);
bufferIndex = 0;
}
} }
private void deflate() throws IOException { private void deflate() throws IOException {
@ -127,72 +112,133 @@ public class ZipOutputStream extends DeflaterOutputStream {
currentEntry.compSize += len; currentEntry.compSize += len;
bytesWritten += len; bytesWritten += len;
if (len > 0) if (len > 0)
out.write(buffer, 0 , len); out.write(buffer, 0, len);
}
private void writeLocalHeader(ZipEntry e) throws IOException {
byte[] tmpBuffer = new byte[30];
addFourBytes(SIGNATURE, 0, tmpBuffer); // local header signature
addTwoBytes(VERSION, 4, tmpBuffer); // version used
addTwoBytes(BITFLAG, 6, tmpBuffer); // flags
addTwoBytes(METHOD, 8, tmpBuffer); // compression method
addFourBytes(e.modTimeDate, 10, tmpBuffer); // last mod date and time
addFourBytes(0, 14, tmpBuffer); // CRC is 0 for local header
// with default flag settings (bit 3 set) the compressed and uncompressed size
// is written here as 0 and written correctly in the data descripter
addFourBytes(0, 18, tmpBuffer); // compressed size
addFourBytes(0, 22, tmpBuffer); // uncompressed size
addTwoBytes(e.name.length(), 26, tmpBuffer); // length of file name
// extra field length, in this implementation extra field in not used
addTwoBytes(0, 28, tmpBuffer);
out.write(tmpBuffer, 0, 30);
// write file name, return the number of bytes written
int len = writeUTF8(e.getName());
bytesWritten += 30 + len;
}
private void writeDataDescriptor(ZipEntry currentEntry) throws IOException {
byte[] tmpBuffer = new byte[16];
addFourBytes(DATA_DESCRIPTER_HEADER, 0, tmpBuffer); // data descripter header
addFourBytes(currentEntry.crc, 4, tmpBuffer); // crc value
addFourBytes(currentEntry.compSize, 8, tmpBuffer); // compressed size
addFourBytes(currentEntry.uncompSize, 12, tmpBuffer);// uncompressed size
out.write(tmpBuffer, 0, 16);
bytesWritten += 16;
} }
private void writeCentralDirectoryHeader(ZipEntry e) throws IOException { private void writeCentralDirectoryHeader(ZipEntry e) throws IOException {
writeFourBytes(CENTRAL_FILE_HEADER); // central directory header signature byte[] tmpBuffer = new byte[46];
writeTwoBytes(VERSION); // version made by
writeTwoBytes(VERSION); // version needed addFourBytes(CENTRAL_FILE_HEADER, 0, tmpBuffer); // central directory header signature
writeTwoBytes(BITFLAG); // flags addTwoBytes(VERSION, 4, tmpBuffer); // version made by
writeTwoBytes(METHOD); // compression method addTwoBytes(VERSION, 6, tmpBuffer); // version needed
addTwoBytes(BITFLAG, 8, tmpBuffer); // flags
addTwoBytes(METHOD, 10, tmpBuffer); // compression method
writeTwoBytes(e.modTime); // last mod time addFourBytes(e.modTimeDate, 12, tmpBuffer); // last mod date and time
writeTwoBytes(e.modDate); // last mod date addFourBytes(e.crc, 16, tmpBuffer); // crc
writeFourBytes(e.crc); // crc addFourBytes(e.compSize, 20, tmpBuffer); // compressed size
writeFourBytes(e.compSize); // compressed size addFourBytes(e.uncompSize, 24, tmpBuffer); // uncompressed size
writeFourBytes(e.uncompSize); // uncompressed size
writeTwoBytes(e.getName().length()); // file name length addTwoBytes(e.getName().length(), 28, tmpBuffer); // file name length
// the following 5 fields are all 0 for a simple default compression // the following 5 fields are all 0 for a simple default compression
writeTwoBytes(0); // extra field length (not used) addTwoBytes(0, 30, tmpBuffer); // extra field length (not used)
writeTwoBytes(0); // comment length (not used) addTwoBytes(0, 32, tmpBuffer); // comment length (not used)
writeTwoBytes(0); // disk number start addTwoBytes(0, 34, tmpBuffer); // disk number start
writeTwoBytes(0); // internal file attribute addTwoBytes(0, 36, tmpBuffer); // internal file attribute
writeFourBytes(0); // external file attribute addFourBytes(0, 38, tmpBuffer); // external file attribute
writeFourBytes((int) e.offset); // relative offset of local header addFourBytes((int) e.offset, 42, tmpBuffer); // relative offset of local header
int len = writeVariableByteLength(e.getName()); out.write(tmpBuffer, 0, 46);
int len = writeUTF8(e.getName());
bytesWritten += 46 + len; bytesWritten += 46 + len;
sizeOfCentralDirectory += 46 + len; sizeOfCentralDirectory += 46 + len;
} }
private void writeEndofCentralDirectory(int offset) throws IOException { private void writeEndofCentralDirectory(int offset) throws IOException {
byte[] tmpBuffer = new byte[22];
short numEntries = (short) entries.size(); short numEntries = (short) entries.size();
writeFourBytes(END_OF_CENTRAL_DIRECTORY_SIG); // end of central directory signature addFourBytes(END_OF_CENTRAL_DIRECTORY_SIG, 0, tmpBuffer); // end of central directory signature
writeTwoBytes(0); // disk number addTwoBytes(0, 4, tmpBuffer); // disk number
writeTwoBytes(0); // disk number where central dir starts addTwoBytes(0, 6, tmpBuffer); // disk number where central dir starts
writeTwoBytes(numEntries); // number of entries on this disk addTwoBytes(numEntries, 8, tmpBuffer); // number of entries on this disk
writeTwoBytes(numEntries); // number of entries in central dir addTwoBytes(numEntries, 10, tmpBuffer); // number of entries in central dir
writeFourBytes(sizeOfCentralDirectory); // length of central directory addFourBytes(sizeOfCentralDirectory, 12, tmpBuffer); // length of central directory
writeFourBytes(offset); // offset of central directory addFourBytes(offset, 16, tmpBuffer); // offset of central directory
writeTwoBytes(0); // length of added comments (not used) addTwoBytes(0, 20, tmpBuffer); // length of added comments (not used)
out.write(tmpBuffer, 0, 22);
bytesWritten += 22; bytesWritten += 22;
} }
@Override
public void close() throws IOException { public void close() throws IOException {
int centralDirOffset = bytesWritten; int centralDirOffset = bytesWritten;
for (ZipEntry e : entries) for (ZipEntry e : entries)
writeCentralDirectoryHeader(e); writeCentralDirectoryHeader(e);
writeEndofCentralDirectory(centralDirOffset); writeEndofCentralDirectory(centralDirOffset);
} deflater.dispose();
out.close();
private void writeTwoBytes(int bytes) throws IOException {
out.write(bytes & 0xff);
out.write((bytes >> 8) & 0xff);
}
private void writeFourBytes(int bytes) throws IOException {
out.write(bytes & 0xff);
out.write((bytes >> 8) & 0xff);
out.write((bytes >> 16) & 0xff);
out.write((bytes >> 24) & 0xff);
} }
private int writeVariableByteLength(String text) throws IOException { @Override
public void flush() throws IOException {
out.write(inputBuffer, 0, inputBuffer.length);
inputBuffer = new byte[INPUT_BUFFER_SIZE];
}
private void finish() throws IOException {
deflater.finish();
while (!deflater.finished()) {
deflate();
}
deflater.reset();
}
private void addTwoBytes(int bytes, int offset, byte[] buffer) throws IOException {
buffer[offset] = (byte) (bytes & 0xff);
buffer[offset + 1] = (byte) ((bytes >> 8) & 0xff);
}
private void addFourBytes(int bytes, int offset, byte[] buffer) throws IOException {
buffer[offset] = (byte) (bytes & 0xff);
buffer[offset + 1] = (byte) ((bytes >> 8) & 0xff);
buffer[offset + 2] = (byte) ((bytes >> 16) & 0xff);
buffer[offset + 3] = (byte) ((bytes >> 24) & 0xff);
}
private int writeUTF8(String text) throws IOException {
byte[] bytes = text.getBytes("UTF-8"); byte[] bytes = text.getBytes("UTF-8");
out.write(bytes, 0, bytes.length); out.write(bytes, 0, bytes.length);
return bytes.length; return bytes.length;