adding zipentry and zipoutputstream classes

This commit is contained in:
Chris Jordan 2013-07-05 14:39:30 -06:00
parent a9d9bc5d20
commit 055f820cac
2 changed files with 354 additions and 15 deletions

View File

@ -1,21 +1,160 @@
/* 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;
public abstract class ZipEntry {
public abstract String getName();
public abstract int getCompressedSize();
public abstract int getSize();
import java.util.Calendar;
import java.util.Date;
/**
* class ZipEntry:
*
* Class to store and retrieve information for entries in a zip file
* Contains variables for all standard zip format field as well as
* setter and accessor methods
*
* @author ReadyTalk Summer 2013 Intern Team
**/
public class ZipEntry {
String name;
//Minimum version needed to extract the file(s) from a compressed state
short reqVersion;
//Method used to compress file
short compressionMethod;
//Format of date and time are both 2 byte fields
short modTime;
short modDate;
//CRC-32
int crc;
//Sizes of file
int compSize;
int uncompSize;
int offset;
public ZipEntry(String name) {
this.name = name;
setTimeDate();
compSize = 0;
uncompSize = 0;
crc = 0;
offset = 0;
}
//Method to return name of the file
public String getName() {
return name;
}
//Method to check if file is a directory
public boolean isDirectory() {
return getName().endsWith("/");
}
}
//Method to return the compressed size of the file
public int getCompressedSize() {
return compSize;
}
//Method to return the uncompressed size of the file
public int getSize() {
return uncompSize;
}
/**
* Method setTime Date():
* Creates a calendar object to retrieve date and time information
*
* Time is stored in the MSDOS format 5 bits for hour, 6 bits for min, 5 bits for seconds
* Hours are in military time and must be adjusted to time zone
* Seconds are divided by two before storing and must be multiplied by two to retrieve
*
* Date is stored in the MSDOS format 7 bit for year, 4 bit for month, 5 bits for day of month
* Year is the number of years since 1980 per, the month is stored starting at 0
* for January. Day of month is stored with nature numbering
*
* Bit masks and shifting are used to build the time and date bytes
*
* @author cjordan
**/
public void setTimeDate(){
final int DAY_OF_MONTH = 5;
final int HOUR_OF_DAY = 11;
final int MINUTE = 12;
final int MONTH = 2;
final int SECOND = 13;
final int YEAR = 1;
Calendar modCalendar = Calendar.getInstance();
//Hour
int timeBits = modCalendar.get(HOUR_OF_DAY);
timeBits = timeBits - 6;
timeBits = timeBits << 6;
//Minutes
int minBits = 0x3f & (modCalendar.get(MINUTE));;
timeBits = timeBits ^ minBits;
timeBits = timeBits << 5;
//Seconds
int secBits = 0x1f & (modCalendar.get(SECOND));
secBits = secBits >> 1;
timeBits = timeBits ^ secBits;
//Store Time
modTime = (short)timeBits;
//Year
int dateBits = (modCalendar.get(YEAR) -1980);
dateBits = dateBits << 4;
//Month
int month = 0xf & ((modCalendar.get(MONTH)) + 1);
dateBits = dateBits ^ month;
dateBits = dateBits << 5;
//Day of month
int dayBits = 0x1f & modCalendar.get(DAY_OF_MONTH);
dateBits = dateBits ^ dayBits;
//Store Date
modDate = (short)dateBits;
}
//Method to set the minimum version required to open the zip file
//Valid values for the compression method are the numbers 1.0 to 10.0
public boolean setRequiredVersion(float versionFloat){
//Check for valid version numbers
if (versionFloat < 1 || versionFloat > 100){
return false;
}
//Convert to short value for storage
versionFloat = versionFloat * 10;
short versionShort = (short)versionFloat;
//Set value of version
reqVersion = versionShort;
return true;
}
//Method to set the compression method for the file
//Valid values for the compression method are the numbers 0 to 19 and 99
public boolean setCompressionMethod(short compMethod){
if (compMethod == 99){
compressionMethod = compMethod;
return true;
}
else if (compMethod < 0 || compMethod > 19){
return false;
}
else{
compressionMethod = compMethod;
return true;
}
}
}

View File

@ -0,0 +1,200 @@
package java.util.zip;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Deflater;
import java.io.BufferedOutputStream;
/**
* An as simple as possible implementation of ZipOutputStream
* Compression method defaults to DEFLATE
* All hardcoded defaults match the defaults for openJDK,
* including PKZip version, bit flags set, compression level, etc
*
* @author ReadyTalk Summer 2013 Intern Team
*/
public class ZipOutputStream extends DeflaterOutputStream {
private static final int SIGNATURE = 0x04034b50;
private static final short VERSION = 0x0014;
private static final short BITFLAG = 0x0008;
private static final short METHOD = 0x0008;
private static final int CENTRAL_FILE_HEADER = 0x02014b50;
private static final int DATA_DESCRIPTER_HEADER = 0x08074b50;
private static final int END_OF_CENTRAL_DIRECTORY_SIG = 0x06054b50;
private static final int DEFAULT_LEVEL = 6;
private final OutputStream out;
private DeflaterOutputStream deflaterStream;
private Deflater deflater;
private List<ZipEntry> entries;
private CRC32 crc = new CRC32();
private ZipEntry currentEntry;
private int bytesWritten;
private int sizeOfCentralDirectory;
private byte[] buffer;
public ZipOutputStream(OutputStream outStream, int bufferSize) {
super(outStream);
out = outStream;
bytesWritten = 0;
sizeOfCentralDirectory = 0;
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 {
e.offset = bytesWritten;
currentEntry = e;
entries.add(e);
writeLocalHeader(e);
}
public void closeEntry() throws IOException {
deflater.finish();
while (!deflater.finished()) {
deflate();
}
deflater.dispose();
deflater.reset();
currentEntry.crc = (int) crc.getValue();
crc.reset();
writeDataDescriptor(currentEntry);
}
private void writeLocalHeader(ZipEntry e) throws IOException {
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 {
currentEntry.uncompSize += length;
crc.update(b, offset, length);
currentEntry.crc = (int) crc.getValue();
deflater.setInput(b, offset, length);
while (deflater.getRemaining() > 0)
deflate();
}
public void write(int b) throws IOException {
byte[] buf = new byte[1];
buf[0] = (byte)(b & 0xff);
write(buf, 0, 1);
}
private void deflate() throws IOException {
int len = deflater.deflate(buffer, 0, buffer.length);
currentEntry.compSize += len;
bytesWritten += len;
if (len > 0)
out.write(buffer, 0 , len);
}
private void writeCentralDirectoryHeader(ZipEntry e) throws IOException {
writeFourBytes(CENTRAL_FILE_HEADER); // central directory header signature
writeTwoBytes(VERSION); // version made by
writeTwoBytes(VERSION); // version needed
writeTwoBytes(BITFLAG); // flags
writeTwoBytes(METHOD); // compression method
writeTwoBytes(e.modTime); // last mod time
writeTwoBytes(e.modDate); // last mod date
writeFourBytes(e.crc); // crc
writeFourBytes(e.compSize); // compressed size
writeFourBytes(e.uncompSize); // uncompressed size
writeTwoBytes(e.getName().length()); // file name length
// the following 5 fields are all 0 for a simple default compression
writeTwoBytes(0); // extra field length (not used)
writeTwoBytes(0); // comment length (not used)
writeTwoBytes(0); // disk number start
writeTwoBytes(0); // internal file attribute
writeFourBytes(0); // external file attribute
writeFourBytes((int) e.offset); // relative offset of local header
int len = writeVariableByteLength(e.getName());
bytesWritten += 46 + len;
sizeOfCentralDirectory += 46 + len;
}
private void writeEndofCentralDirectory(int offset) throws IOException {
short numEntries = (short) entries.size();
writeFourBytes(END_OF_CENTRAL_DIRECTORY_SIG); // end of central directory signature
writeTwoBytes(0); // disk number
writeTwoBytes(0); // disk number where central dir starts
writeTwoBytes(numEntries); // number of entries on this disk
writeTwoBytes(numEntries); // number of entries in central dir
writeFourBytes(sizeOfCentralDirectory); // length of central directory
writeFourBytes(offset); // offset of central directory
writeTwoBytes(0); // length of added comments (not used)
bytesWritten += 22;
}
public void close() throws IOException {
int centralDirOffset = bytesWritten;
for (ZipEntry e : entries)
writeCentralDirectoryHeader(e);
writeEndofCentralDirectory(centralDirOffset);
}
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 {
byte[] bytes = text.getBytes("UTF-8");
out.write(bytes, 0, bytes.length);
return bytes.length;
}
}