package de.idyl.winzipaes.impl; import java.io.FileInputStream; import java.io.IOException; import java.util.Arrays; import java.util.logging.Logger; import java.util.zip.ZipEntry; /** * Provide InputStream access to <b>compressed data</b> from one ZipEntry contained * within one ZipFile. Necessary as java.util.zip.ZipInputStream only provides access to * the <b>uncompressed data</b>. * * @author olaf@merkert.de */ public class ZipFileEntryInputStream implements ZipConstants { private static final Logger LOG = Logger.getLogger(ZipFileEntryInputStream.class.getName()); protected FileInputStream fis; protected long startPos; protected long endPos; protected long currentPos; protected long compressedSize; public long getCompressedSize() { return this.compressedSize; } public ZipFileEntryInputStream( String fileName ) throws IOException { fis = new FileInputStream(fileName); } /** * position input stream to start of ZipEntry this instance was created for * * @throws IOException */ public void nextEntry( ZipEntry ze ) throws IOException { LOG.fine("nextEntry().currentPos=" + currentPos); byte[] intBuffer = new byte[4]; int bytesRead = fis.read(intBuffer); LOG.fine("bytes read="+bytesRead); if( bytesRead==-1 ) { // this occurred on android once, with FileInputStream as my superclass throw new IOException("no data available - available=" + fis.available()); } int dataDescriptorLength = 0; if( Arrays.equals(intBuffer, new byte[] { 0x50, 0x4b, 0x07, 0x08 }) ) { // header does not belong to next file, but is start of the "data descriptor" of last file // skip this data descriptor containing crc32(4), compressedSize(4), uncompressedSize(4) dataDescriptorLength = 4 + 4 + 4; fis.skip( dataDescriptorLength ); // read local file header signature fis.read(intBuffer); } if( !Arrays.equals(intBuffer, new byte[] { 0x50, 0x4b, 0x03, 0x04 }) ) { throw new IOException("wrong local file header signature - value=" + ByteArrayHelper.toString(intBuffer) ); } // info only - if bit-3 is set, current entry is followed by data descriptor boolean hasDataDescriptor = (ze.getMethod() & 8) > 0; LOG.fine( "nextEntry().hasDataDescriptor=" + hasDataDescriptor ); this.compressedSize = ze.getCompressedSize(); fis.skip(14 + 4 + 4); // 14 + localFileHeaderSignature(4) + compressedSize(4) + size(4) byte[] shortBuffer = new byte[2]; fis.read(shortBuffer); int fileNameLength = ByteArrayHelper.toInt(shortBuffer); fis.read(shortBuffer); int extraFieldLength = ByteArrayHelper.toInt(shortBuffer); startPos = 18 + 12 + fileNameLength + extraFieldLength + dataDescriptorLength; currentPos = startPos; endPos = startPos + this.compressedSize; fis.skip( fileNameLength + extraFieldLength ); } // should work without this, but never trust an OO system public int read( byte[] b ) throws IOException { return this.read(b, 0, b.length); } public int read( byte[] b, int off, int len ) throws IOException { int bytesRead = -1; int remainingBytes = (int) (endPos - currentPos); if( remainingBytes > 0 ) { if( currentPos + len < endPos ) { bytesRead = fis.read(b, off, len); currentPos += bytesRead; } else { bytesRead = fis.read(b, off, remainingBytes); currentPos += bytesRead; } } return bytesRead; } public void close() throws IOException { fis.close(); } }