/// <summary> /// Compress the specified byte[] and return the compressed byte[] /// </summary> /// <param name="uncompressedBytes"></param> /// <param name="entryName"></param> /// <returns></returns> public static byte[] CompressStream( byte[] uncompressedValue, string entryExtension ) { byte[] compressedBytes; using ( MemoryStream compressedStream = new MemoryStream() ) { // Compress the raw bytes into the compressed stream using ( ZipOutputStream outStream = new ZipOutputStream( compressedStream ) ) { outStream.SetLevel( 9 ); // 0 - store only to 9 - means best compression ZipEntry entry = new ZipEntry( string.Concat( "report.", entryExtension ) ); outStream.PutNextEntry( entry ); outStream.Write( uncompressedValue, 0, uncompressedValue.Length ); outStream.Flush(); outStream.Finish(); // Check the length for this ZipOutputStream long streamLength = outStream.Length; // read the compressed stream's buffer compressedBytes = new byte[ streamLength ]; compressedStream.Seek( 0, SeekOrigin.Begin ); // Note: There is potential data loss here. If the compressed stream length // is > 2Gig, we will have an invalid length, and the file will be // corrupt. Rather than adding complexity for that corner case, I'm // just noting the possibility, and using the cast. compressedStream.Read( compressedBytes, 0, (int)streamLength ); } } return compressedBytes; }
/// <summary> /// Starts a new Zip entry. It automatically closes the previous /// entry if present. If the compression method is stored, the entry /// must have a valid size and crc, otherwise all elements (except /// name) are optional, but must be correct if present. If the time /// is not set in the entry, the current time is used. /// </summary> /// <param name="entry"> /// the entry. /// </param> /// <exception cref="System.IO.IOException"> /// if an I/O error occured. /// </exception> /// <exception cref="System.InvalidOperationException"> /// if stream was finished /// </exception> public void PutNextEntry(ZipEntry entry) { if (entries == null) { throw new InvalidOperationException("ZipOutputStream was finished"); } CompressionMethod method = entry.CompressionMethod; int flags = 0; entry.IsCrypted = Password != null; switch (method) { case CompressionMethod.Stored: if (entry.CompressedSize >= 0) { if (entry.Size < 0) { entry.Size = entry.CompressedSize; } else if (entry.Size != entry.CompressedSize) { throw new ZipException("Method STORED, but compressed size != size"); } } else { entry.CompressedSize = entry.Size; } if (entry.IsCrypted) { entry.CompressedSize += 12; } if (entry.Size < 0) { throw new ZipException("Method STORED, but size not set"); } else if (entry.Crc < 0) { throw new ZipException("Method STORED, but crc not set"); } break; case CompressionMethod.Deflated: if (entry.CompressedSize < 0 || entry.Size < 0 || entry.Crc < 0) { flags |= 8; } break; } if (curEntry != null) { CloseEntry(); } // if (entry.DosTime < 0) { // entry.Time = System.Environment.TickCount; // } if (entry.IsCrypted) { flags |= 1; } entry.Flags = flags; entry.Offset = offset; entry.CompressionMethod = (CompressionMethod)method; curMethod = method; // Write the local file header WriteLeInt(ZipConstants.LOCSIG); // write ZIP version WriteLeShort(method == CompressionMethod.Stored ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION); if ((flags & 8) == 0) { WriteLeShort(flags); WriteLeShort((byte)method); WriteLeInt((int)entry.DosTime); WriteLeInt((int)entry.Crc); WriteLeInt((int)entry.CompressedSize); WriteLeInt((int)entry.Size); } else { if (baseOutputStream.CanSeek) { shouldWriteBack = true; WriteLeShort((short)(flags & ~8)); } else { shouldWriteBack = false; WriteLeShort(flags); } WriteLeShort((byte)method); WriteLeInt((int)entry.DosTime); if (baseOutputStream.CanSeek) { seekPos = baseOutputStream.Position; } WriteLeInt(0); WriteLeInt(0); WriteLeInt(0); } byte[] name = ZipConstants.ConvertToArray(entry.Name); if (name.Length > 0xFFFF) { throw new ZipException("Name too long."); } byte[] extra = entry.ExtraData; if (extra == null) { extra = new byte[0]; } if (extra.Length > 0xFFFF) { throw new ZipException("Extra data too long."); } WriteLeShort(name.Length); WriteLeShort(extra.Length); baseOutputStream.Write(name, 0, name.Length); baseOutputStream.Write(extra, 0, extra.Length); if (Password != null) { InitializePassword(Password); byte[] cryptbuffer = new byte[12]; Random rnd = new Random(); for (int i = 0; i < cryptbuffer.Length; ++i) { cryptbuffer[i] = (byte)rnd.Next(); } EncryptBlock(cryptbuffer, 0, cryptbuffer.Length); baseOutputStream.Write(cryptbuffer, 0, cryptbuffer.Length); } offset += ZipConstants.LOCHDR + name.Length + extra.Length; /* Activate the entry. */ curEntry = entry; crc.Reset(); if (method == CompressionMethod.Deflated) { def.Reset(); } size = 0; }
/// <summary> /// Closes the current entry. /// </summary> /// <exception cref="System.IO.IOException"> /// if an I/O error occured. /// </exception> /// <exception cref="System.InvalidOperationException"> /// if no entry is active. /// </exception> public void CloseEntry() { if (curEntry == null) { throw new InvalidOperationException("No open entry"); } /* First finish the deflater, if appropriate */ if (curMethod == CompressionMethod.Deflated) { base.Finish(); } int csize = curMethod == CompressionMethod.Deflated ? def.TotalOut : size; if (curEntry.Size < 0) { curEntry.Size = size; } else if (curEntry.Size != size) { throw new ZipException("size was " + size + ", but I expected " + curEntry.Size); } if (curEntry.IsCrypted) { csize += 12; } if (curEntry.CompressedSize < 0) { curEntry.CompressedSize = csize; } else if (curEntry.CompressedSize != csize) { throw new ZipException("compressed size was " + csize + ", but I expected " + curEntry.CompressedSize); } if (curEntry.Crc < 0) { curEntry.Crc = crc.Value; } else if (curEntry.Crc != crc.Value) { throw new ZipException("crc was " + crc.Value + ", but I expected " + curEntry.Crc); } offset += csize; /* Now write the data descriptor entry if needed. */ if (curMethod == CompressionMethod.Deflated && (curEntry.Flags & 8) != 0) { if (shouldWriteBack) { curEntry.Flags &= ~8; long curPos = baseOutputStream.Position; baseOutputStream.Seek(seekPos, SeekOrigin.Begin); WriteLeInt((int)curEntry.Crc); WriteLeInt((int)curEntry.CompressedSize); WriteLeInt((int)curEntry.Size); baseOutputStream.Seek(curPos, SeekOrigin.Begin); shouldWriteBack = false; } else { WriteLeInt(ZipConstants.EXTSIG); WriteLeInt((int)curEntry.Crc); WriteLeInt((int)curEntry.CompressedSize); WriteLeInt((int)curEntry.Size); offset += ZipConstants.EXTHDR; } } entries.Add(curEntry); curEntry = null; }
public ZipEntryEnumeration(ZipEntry[] arr) { array = arr; }
/// <summary> /// Read the central directory of a zip file and fill the entries /// array. This is called exactly once by the constructors. /// </summary> /// <exception name="System.IO.IOException"> /// if a i/o error occured. /// </exception> /// <exception name="Tools.FileCompressionUtilities.ZipException"> /// if the central directory is malformed /// </exception> void ReadEntries() { /* Search for the End Of Central Directory. When a zip comment is * present the directory may start earlier. * FIXME: This searches the whole file in a very slow manner if the * file isn't a zip file. */ long pos = baseStream.Length - ZipConstants.ENDHDR; do { if (pos < 0) { throw new ZipException("central directory not found, probably not a zip file"); } baseStream.Seek(pos--, SeekOrigin.Begin); } while (ReadLeInt() != ZipConstants.ENDSIG); long oldPos = baseStream.Position; baseStream.Position += ZipConstants.ENDTOT - ZipConstants.ENDNRD; if (baseStream.Position - oldPos != ZipConstants.ENDTOT - ZipConstants.ENDNRD) { throw new EndOfStreamException(); } int count = ReadLeShort(); oldPos = baseStream.Position; baseStream.Position += ZipConstants.ENDOFF - ZipConstants.ENDSIZ; if (baseStream.Position - oldPos != ZipConstants.ENDOFF - ZipConstants.ENDSIZ) { throw new EndOfStreamException(); } int centralOffset = ReadLeInt(); // GET COMMENT SIZE (COMES AFTER CENTRALOFFSET) int commentSize = ReadLeShort(); byte[] zipComment = new byte[commentSize]; baseStream.Read(zipComment, 0, zipComment.Length); comment = ZipConstants.ConvertToString(zipComment); entries = new ZipEntry[count]; baseStream.Seek(centralOffset, SeekOrigin.Begin); for (int i = 0; i < count; i++) { if (ReadLeInt() != ZipConstants.CENSIG) { throw new ZipException("Wrong Central Directory signature"); } oldPos = baseStream.Position; baseStream.Position += ZipConstants.CENHOW - ZipConstants.CENVEM; if (baseStream.Position - oldPos != ZipConstants.CENHOW - ZipConstants.CENVEM) { throw new EndOfStreamException(); } int method = ReadLeShort(); int dostime = ReadLeInt(); int crc = ReadLeInt(); int csize = ReadLeInt(); int size = ReadLeInt(); int nameLen = ReadLeShort(); int extraLen = ReadLeShort(); int commentLen = ReadLeShort(); oldPos = baseStream.Position; baseStream.Position += ZipConstants.CENOFF - ZipConstants.CENDSK; if (baseStream.Position - oldPos != ZipConstants.CENOFF - ZipConstants.CENDSK) { throw new EndOfStreamException(); } int offset = ReadLeInt(); byte[] buffer = new byte[Math.Max(nameLen, commentLen)]; baseStream.Read(buffer, 0, nameLen); string name = ZipConstants.ConvertToString(buffer); ZipEntry entry = new ZipEntry(name); entry.CompressionMethod = (CompressionMethod)method; entry.Crc = crc & 0xffffffffL; entry.Size = size & 0xffffffffL; entry.CompressedSize = csize & 0xffffffffL; entry.DosTime = (uint)dostime; if (extraLen > 0) { byte[] extra = new byte[extraLen]; baseStream.Read(extra, 0, extraLen); entry.ExtraData = extra; } if (commentLen > 0) { baseStream.Read(buffer, 0, commentLen); entry.Comment = ZipConstants.ConvertToString(buffer); } entry.ZipFileIndex = i; entry.Offset = offset; entries[i] = entry; } }
/// <summary> /// Checks, if the local header of the entry at index i matches the /// central directory, and returns the offset to the data. /// </summary> /// <returns> /// the start offset of the (compressed) data. /// </returns> /// <exception name="System.IO.IOException"> /// if a i/o error occured. /// </exception> /// <exception name="Tools.FileCompressionUtilities.ZipException"> /// if the local header doesn't match the central directory header /// </exception> long CheckLocalHeader(ZipEntry entry) { lock(baseStream) { baseStream.Seek(entry.Offset, SeekOrigin.Begin); if (ReadLeInt() != ZipConstants.LOCSIG) { throw new ZipException("Wrong Local header signature"); } /* skip version and flags */ long oldPos = baseStream.Position; baseStream.Position += ZipConstants.LOCHOW - ZipConstants.LOCVER; if (baseStream.Position - oldPos != ZipConstants.LOCHOW - ZipConstants.LOCVER) { throw new EndOfStreamException(); } if (entry.CompressionMethod != (CompressionMethod)ReadLeShort()) { throw new ZipException("Compression method mismatch"); } /* Skip time, crc, size and csize */ oldPos = baseStream.Position; baseStream.Position += ZipConstants.LOCNAM - ZipConstants.LOCTIM; if (baseStream.Position - oldPos != ZipConstants.LOCNAM - ZipConstants.LOCTIM) { throw new EndOfStreamException(); } if (entry.Name.Length != ReadLeShort()) { throw new ZipException("file name length mismatch"); } int extraLen = entry.Name.Length + ReadLeShort(); return entry.Offset + ZipConstants.LOCHDR + extraLen; } }
/// <summary> /// Creates an input stream reading the given zip entry as /// uncompressed data. Normally zip entry should be an entry /// returned by GetEntry(). /// </summary> /// <returns> /// the input stream. /// </returns> /// <exception name="System.IO.IOException"> /// if a i/o error occured. /// </exception> /// <exception name="Tools.FileCompressionUtilities.ZipException"> /// if the Zip archive is malformed. /// </exception> public Stream GetInputStream(ZipEntry entry) { if (entries == null) { throw new InvalidOperationException("ZipFile has closed"); } int index = entry.ZipFileIndex; if (index < 0 || index >= entries.Length || entries[index].Name != entry.Name) { index = GetEntryIndex(entry.Name); if (index < 0) { throw new IndexOutOfRangeException(); } } long start = CheckLocalHeader(entries[index]); CompressionMethod method = entries[index].CompressionMethod; Stream istr = new PartialInputStream(baseStream, start, entries[index].CompressedSize); switch (method) { case CompressionMethod.Stored: return istr; case CompressionMethod.Deflated: return new InflaterInputStream(istr, new Inflater(true)); default: throw new ZipException("Unknown compression method " + method); } }
/// <summary> /// Creates a copy of the given zip entry. /// </summary> /// <param name="e"> /// the entry to copy. /// </param> public ZipEntry(ZipEntry e) { name = e.name; known = e.known; size = e.size; compressedSize = e.compressedSize; crc = e.crc; dosTime = e.dosTime; method = e.method; extra = e.extra; comment = e.comment; }
/// <summary> /// Reads a block of bytes from the current zip entry. /// </summary> /// <returns> /// the number of bytes read (may be smaller, even before EOF), or -1 on EOF. /// </returns> /// <exception name="Exception"> /// IOException if a i/o error occured. /// ZipException if the deflated stream is corrupted. /// </exception> public override int Read(byte[] b, int off, int len) { if (crc == null) { throw new InvalidOperationException("Closed."); } if (entry == null) { return 0; } bool finished = false; switch (method) { case ZipOutputStream.DEFLATED: len = base.Read(b, off, len); if (len <= 0) { // TODO BUG1 -jr- Check this was < 0 but avail was not adjusted causing failure in later calls if (!inf.IsFinished) { throw new ZipException("Inflater not finished!?"); } avail = inf.RemainingInput; // BUG1 -jr- With bit 3 set you dont yet know the size if ((flags & 8) == 0 && (inf.TotalIn != csize || inf.TotalOut != size)) { throw new ZipException("size mismatch: " + csize + ";" + size + " <-> " + inf.TotalIn + ";" + inf.TotalOut); } inf.Reset(); finished = true; } break; case ZipOutputStream.STORED: if (len > csize && csize >= 0) { len = (int)csize; } len = ReadBuf(b, off, len); if (len > 0) { csize -= len; size -= len; } if (csize == 0) { finished = true; } else { if (len < 0) { throw new ZipException("EOF in stored block"); } } // decrypting crypted data if (cryptbuffer != null) { DecryptBlock(b, off, len); } break; } if (len > 0) { crc.Update(b, off, len); } if (finished) { if ((flags & 8) != 0) { ReadDataDescr(); } if ((crc.Value & 0xFFFFFFFFL) != entry.Crc && entry.Crc != -1) { throw new ZipException("CRC mismatch"); } crc.Reset(); entry = null; } return len; }
/// <summary> /// Open the next entry from the zip archive, and return its description. /// If the previous entry wasn't closed, this method will close it. /// </summary> public ZipEntry GetNextEntry() { if (crc == null) { throw new InvalidOperationException("Closed."); } if (entry != null) { CloseEntry(); } if (this.cryptbuffer != null) { if (avail == 0 && inf.RemainingInput != 0) { avail = inf.RemainingInput - 16; inf.Reset(); } baseInputStream.Position -= this.len; baseInputStream.Read(this.buf, 0, this.len); } int header = ReadLeInt(); // -jr- added end sig for empty zip files, Zip64 end sig and digital sig for files that have them... if (header == ZipConstants.CENSIG || header == ZipConstants.ENDSIG || header == ZipConstants.CENDIGITALSIG || header == ZipConstants.CENSIG64) { // Central Header reached or end of empty zip file Close(); return null; } // -jr- 07-Dec-2003 ignore spanning temporary signatures if found // SPANNINGSIG is same as descriptor signature and is untested as yet. if (header == ZipConstants.SPANTEMPSIG || header == ZipConstants.SPANNINGSIG) { header = ReadLeInt(); } if (header != ZipConstants.LOCSIG) { throw new ZipException("Wrong Local header signature: 0x" + String.Format("{0:X}", header)); } short version = (short)ReadLeShort(); flags = ReadLeShort(); method = ReadLeShort(); uint dostime = (uint)ReadLeInt(); int crc2 = ReadLeInt(); csize = ReadLeInt(); size = ReadLeInt(); int nameLen = ReadLeShort(); int extraLen = ReadLeShort(); bool isCrypted = (flags & 1) == 1; if (method == ZipOutputStream.STORED && (!isCrypted && csize != size || (isCrypted && csize - 12 != size))) { throw new ZipException("Stored, but compressed != uncompressed"); } byte[] buffer = new byte[nameLen]; ReadFully(buffer); string name = ZipConstants.ConvertToString(buffer); entry = new ZipEntry(name); entry.IsCrypted = isCrypted; entry.Version = (ushort)version; if (method != 0 && method != 8) { throw new ZipException("unknown compression method " + method); } entry.CompressionMethod = (CompressionMethod)method; if ((flags & 8) == 0) { entry.Crc = crc2 & 0xFFFFFFFFL; entry.Size = size & 0xFFFFFFFFL; entry.CompressedSize = csize & 0xFFFFFFFFL; } entry.DosTime = dostime; if (extraLen > 0) { byte[] extra = new byte[extraLen]; ReadFully(extra); entry.ExtraData = extra; } // test for encryption if (isCrypted) { if (password == null) { throw new ZipException("No password set."); } InitializePassword(password); cryptbuffer = new byte[12]; ReadFully(cryptbuffer); DecryptBlock(cryptbuffer, 0, cryptbuffer.Length); if ((flags & 8) == 0) {// -jr- 10-Feb-2004 Dont yet know correct size here.... csize -= 12; } } else { cryptbuffer = null; } if (method == ZipOutputStream.DEFLATED && avail > 0) { System.Array.Copy(buf, len - (int)avail, buf, 0, (int)avail); len = (int)avail; avail = 0; if (isCrypted) { DecryptBlock(buf, 0, Math.Min((int)csize, len)); } inf.SetInput(buf, 0, len); } return entry; }
/// <summary> /// Closes the current zip entry and moves to the next one. /// </summary> public void CloseEntry() { if (crc == null) { throw new InvalidOperationException("Closed."); } if (entry == null) { return; } if (method == ZipOutputStream.DEFLATED) { if ((flags & 8) != 0) { /* We don't know how much we must skip, read until end. */ byte[] tmp = new byte[2048]; while (Read(tmp, 0, tmp.Length) > 0) ; /* read will close this entry */ return; } csize -= inf.TotalIn; avail = inf.RemainingInput; } if (avail > csize && csize >= 0) { avail -= csize; } else { csize -= avail; avail = 0; while (csize != 0) { int skipped = (int)base.Skip(csize & 0xFFFFFFFFL); if (skipped <= 0) { throw new ZipException("zip archive ends early."); } csize -= skipped; } } size = 0; crc.Reset(); if (method == ZipOutputStream.DEFLATED) { inf.Reset(); } entry = null; }
/// <summary> /// Closes the zip file. /// </summary> /// <exception name="Exception"> /// if a i/o error occured. /// </exception> public override void Close() { base.Close(); crc = null; entry = null; }