Пример #1
0
        /// <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;
        }
Пример #2
0
        /// <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);
                }
            }
        }