/// <summary> /// Finishes the stream. This will write the central directory at the /// end of the zip file and flush the stream. /// </summary> /// <remarks> /// This is automatically called when the stream is closed. /// </remarks> /// <exception cref="System.IO.IOException"> /// An I/O error occurs. /// </exception> /// <exception cref="ZipException"> /// Comment exceeds the maximum length<br/> /// Entry name exceeds the maximum length /// </exception> public override void Finish() { if (entries == null) { return; } if (curEntry != null) { CloseEntry(); } int numEntries = 0; int sizeEntries = 0; foreach (ZipEntry entry in entries) { CompressionMethod method = entry.CompressionMethod; WriteLeInt(ZipConstants.CENSIG); WriteLeShort(ZipConstants.VERSION_MADE_BY); WriteLeShort(entry.Version); WriteLeShort(entry.Flags); WriteLeShort((short)method); WriteLeInt((int)entry.DosTime); WriteLeInt((int)entry.Crc); WriteLeInt((int)entry.CompressedSize); WriteLeInt((int)entry.Size); 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]; } byte[] entryComment = entry.Comment != null?ZipConstants.ConvertToArray(entry.Comment) : new byte[0]; if (entryComment.Length > 0xffff) { throw new ZipException("Comment too long."); } WriteLeShort(name.Length); WriteLeShort(extra.Length); WriteLeShort(entryComment.Length); WriteLeShort(0); // disk number WriteLeShort(0); // internal file attr // external file attribute if (entry.ExternalFileAttributes != -1) { WriteLeInt(entry.ExternalFileAttributes); } else { if (entry.IsDirectory) // mark entry as directory (from nikolam.AT.perfectinfo.com) { WriteLeInt(16); } else { WriteLeInt(0); } } WriteLeInt(entry.Offset); baseOutputStream.Write(name, 0, name.Length); baseOutputStream.Write(extra, 0, extra.Length); baseOutputStream.Write(entryComment, 0, entryComment.Length); ++numEntries; sizeEntries += ZipConstants.CENHDR + name.Length + extra.Length + entryComment.Length; } WriteLeInt(ZipConstants.ENDSIG); WriteLeShort(0); // number of this disk WriteLeShort(0); // no of disk with start of central dir WriteLeShort(numEntries); // entries in central dir for this disk WriteLeShort(numEntries); // total entries in central directory WriteLeInt(sizeEntries); // size of the central directory WriteLeInt((int)offset); // offset of start of central dir WriteLeShort(zipComment.Length); baseOutputStream.Write(zipComment, 0, zipComment.Length); baseOutputStream.Flush(); entries = null; }
/// <summary> /// Starts a new Zip entry. It automatically closes the previous /// entry if present. /// All entry elements bar name are optional, but must be correct if present. /// If the compression method is stored and the output is not patchable /// the compression for that entry is automatically changed to deflate level 0 /// </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> /// <exception cref="ZipException"> /// Too many entries in the Zip file<br/> /// Entry name is too long<br/> /// Finish has already been called<br/> /// </exception> public void PutNextEntry(ZipEntry entry) { if (entries == null) { throw new InvalidOperationException("ZipOutputStream was finished"); } if (curEntry != null) { CloseEntry(); } if (entries.Count >= 0xffff) { throw new ZipException("Too many entries for Zip file"); } CompressionMethod method = entry.CompressionMethod; int compressionLevel = defaultCompressionLevel; entry.Flags = 0; patchEntryHeader = false; bool headerInfoAvailable = true; if (method == 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 { if (entry.Size >= 0) { entry.CompressedSize = entry.Size; } } if (entry.Size < 0 || entry.Crc < 0) { if (CanPatchEntries == true) { headerInfoAvailable = false; } else { // Cant patch entries so storing is not possible. method = CompressionMethod.Deflated; compressionLevel = 0; } } } if (method == CompressionMethod.Deflated) { if (entry.Size == 0) { // No need to compress - no data. entry.CompressedSize = entry.Size; entry.Crc = 0; method = CompressionMethod.Stored; } else if (entry.CompressedSize < 0 || entry.Size < 0 || entry.Crc < 0) { headerInfoAvailable = false; } } if (headerInfoAvailable == false) { if (CanPatchEntries == false) { entry.Flags |= 8; } else { patchEntryHeader = true; } } if (Password != null) { entry.IsCrypted = true; if (entry.Crc < 0) { // Need to append data descriptor as crc is used for encryption and its not known. entry.Flags |= 8; } } entry.Offset = (int)offset; entry.CompressionMethod = (CompressionMethod)method; curMethod = method; // Write the local file header WriteLeInt(ZipConstants.LOCSIG); WriteLeShort(entry.Version); WriteLeShort(entry.Flags); WriteLeShort((byte)method); WriteLeInt((int)entry.DosTime); if (headerInfoAvailable == true) { WriteLeInt((int)entry.Crc); WriteLeInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CRYPTO_HEADER_SIZE : (int)entry.CompressedSize); WriteLeInt((int)entry.Size); } else { if (patchEntryHeader == true) { headerPatchPos = baseOutputStream.Position; } WriteLeInt(0); // Crc WriteLeInt(0); // Compressed size WriteLeInt(0); // Uncompressed size } byte[] name = ZipConstants.ConvertToArray(entry.Name); if (name.Length > 0xFFFF) { throw new ZipException("Entry 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); offset += ZipConstants.LOCHDR + name.Length + extra.Length; // Activate the entry. curEntry = entry; crc.Reset(); if (method == CompressionMethod.Deflated) { def.Reset(); def.SetLevel(compressionLevel); } size = 0; if (entry.IsCrypted == true) { if (entry.Crc < 0) // so testing Zip will says its ok { WriteEncryptionHeader(entry.DosTime << 16); } else { WriteEncryptionHeader(entry.Crc); } } }