Beispiel #1
0
        /// <summary>
        /// Creates a zip archive or chain of zip archives.
        /// </summary>
        /// <param name="streamContext">A context interface to handle opening
        /// and closing of archive and file streams.</param>
        /// <param name="files">An array of file lists.  Each list is
        /// compressed into one stream in the archive.</param>
        /// <param name="maxArchiveSize">The maximum number of bytes for one archive
        /// before the contents are chained to the next archive, or zero for unlimited
        /// archive size.</param>
        /// <exception cref="ArchiveException">The archive could not be
        /// created.</exception>
        /// <remarks>
        /// The stream context implementation may provide a mapping from the file
        /// paths within the archive to the external file paths.
        /// </remarks>
        public override void Pack(
            IPackStreamContext streamContext,
            IEnumerable <string> files,
            long maxArchiveSize)
        {
            if (streamContext == null)
            {
                throw new ArgumentNullException("streamContext");
            }

            if (files == null)
            {
                throw new ArgumentNullException("files");
            }

            lock (this)
            {
                Stream archiveStream = null;
                try
                {
                    this.ResetProgressData();
                    this.totalArchives = 1;

                    object forceZip64Value = streamContext.GetOption("forceZip64", null);
                    bool   forceZip64      = Convert.ToBoolean(
                        forceZip64Value, CultureInfo.InvariantCulture);

                    // Count the total number of files and bytes to be compressed.
                    foreach (string file in files)
                    {
                        FileAttributes attributes;
                        DateTime       lastWriteTime;
                        Stream         fileStream = streamContext.OpenFileReadStream(
                            file,
                            out attributes,
                            out lastWriteTime);
                        if (fileStream != null)
                        {
                            this.totalFileBytes += fileStream.Length;
                            this.totalFiles++;
                            streamContext.CloseFileReadStream(file, fileStream);
                        }
                    }

                    List <ZipFileHeader> fileHeaders = new List <ZipFileHeader>();
                    this.currentFileNumber = -1;

                    if (this.currentArchiveName == null)
                    {
                        this.mainArchiveName    = streamContext.GetArchiveName(0);
                        this.currentArchiveName = this.mainArchiveName;

                        if (String.IsNullOrEmpty(this.currentArchiveName))
                        {
                            throw new FileNotFoundException("No name provided for archive.");
                        }
                    }

                    this.OnProgress(ArchiveProgressType.StartArchive);

                    // Compress files one by one, saving header info for each.
                    foreach (string file in files)
                    {
                        ZipFileHeader fileHeader = this.PackOneFile(
                            streamContext,
                            file,
                            maxArchiveSize,
                            forceZip64,
                            ref archiveStream);

                        if (fileHeader != null)
                        {
                            fileHeaders.Add(fileHeader);
                        }

                        this.currentArchiveTotalBytes = (archiveStream != null ?
                                                         archiveStream.Position : 0);
                        this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
                    }

                    bool zip64 = forceZip64 || this.totalFiles > UInt16.MaxValue;

                    // Write the central directory composed of all the file headers.
                    uint centralDirStartArchiveNumber = 0;
                    long centralDirStartPosition      = 0;
                    long centralDirSize = 0;
                    for (int i = 0; i < fileHeaders.Count; i++)
                    {
                        ZipFileHeader fileHeader = fileHeaders[i];

                        int headerSize = fileHeader.GetSize(true);
                        centralDirSize += headerSize;

                        this.CheckArchiveWriteStream(
                            streamContext,
                            maxArchiveSize,
                            headerSize,
                            ref archiveStream);

                        if (i == 0)
                        {
                            centralDirStartArchiveNumber = (uint)this.currentArchiveNumber;
                            centralDirStartPosition      = archiveStream.Position;
                        }

                        fileHeader.Write(archiveStream, true);
                        if (fileHeader.zip64)
                        {
                            zip64 = true;
                        }
                    }

                    this.currentArchiveTotalBytes =
                        (archiveStream != null ? archiveStream.Position : 0);
                    this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;

                    ZipEndOfCentralDirectory eocd = new ZipEndOfCentralDirectory();
                    eocd.dirStartDiskNumber = centralDirStartArchiveNumber;
                    eocd.entriesOnDisk      = fileHeaders.Count;
                    eocd.totalEntries       = fileHeaders.Count;
                    eocd.dirSize            = centralDirSize;
                    eocd.dirOffset          = centralDirStartPosition;
                    eocd.comment            = this.comment;

                    Zip64EndOfCentralDirectoryLocator eocdl =
                        new Zip64EndOfCentralDirectoryLocator();

                    int maxFooterSize = eocd.GetSize(false);
                    if (archiveStream != null && (zip64 || archiveStream.Position >
                                                  ((long)UInt32.MaxValue) - eocd.GetSize(false)))
                    {
                        maxFooterSize += eocd.GetSize(true) + (int)
                                         Zip64EndOfCentralDirectoryLocator.EOCDL64_SIZE;
                        zip64 = true;
                    }

                    this.CheckArchiveWriteStream(
                        streamContext,
                        maxArchiveSize,
                        maxFooterSize,
                        ref archiveStream);
                    eocd.diskNumber = (uint)this.currentArchiveNumber;

                    if (zip64)
                    {
                        eocd.versionMadeBy       = 45;
                        eocd.versionNeeded       = 45;
                        eocd.zip64               = true;
                        eocdl.dirOffset          = archiveStream.Position;
                        eocdl.dirStartDiskNumber = (uint)this.currentArchiveNumber;
                        eocdl.totalDisks         = (uint)this.currentArchiveNumber + 1;
                        eocd.Write(archiveStream);
                        eocdl.Write(archiveStream);

                        eocd.dirOffset          = UInt32.MaxValue;
                        eocd.dirStartDiskNumber = UInt16.MaxValue;
                    }

                    eocd.zip64 = false;
                    eocd.Write(archiveStream);

                    this.currentArchiveTotalBytes     = archiveStream.Position;
                    this.currentArchiveBytesProcessed = this.currentArchiveTotalBytes;
                }
                finally
                {
                    if (archiveStream != null)
                    {
                        streamContext.CloseArchiveWriteStream(
                            this.currentArchiveNumber, this.mainArchiveName, archiveStream);
                        this.OnProgress(ArchiveProgressType.FinishArchive);
                    }
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Adds one file to a zip archive in the process of being created.
        /// </summary>
        private ZipFileHeader PackOneFile(
            IPackStreamContext streamContext,
            string file,
            long maxArchiveSize,
            bool forceZip64,
            ref Stream archiveStream)
        {
            Stream fileStream          = null;
            int    headerArchiveNumber = 0;

            try
            {
                // TODO: call GetOption to get compression method for the specific file
                ZipCompressionMethod compressionMethod = ZipCompressionMethod.Deflate;
                if (this.CompressionLevel == WixToolset.Dtf.Compression.CompressionLevel.None)
                {
                    compressionMethod = ZipCompressionMethod.Store;
                }

                Converter <Stream, Stream> compressionStreamCreator;
                if (!ZipEngine.compressionStreamCreators.TryGetValue(
                        compressionMethod, out compressionStreamCreator))
                {
                    return(null);
                }

                FileAttributes attributes;
                DateTime       lastWriteTime;
                fileStream = streamContext.OpenFileReadStream(
                    file, out attributes, out lastWriteTime);
                if (fileStream == null)
                {
                    return(null);
                }

                this.currentFileName = file;
                this.currentFileNumber++;

                this.currentFileTotalBytes     = fileStream.Length;
                this.currentFileBytesProcessed = 0;
                this.OnProgress(ArchiveProgressType.StartFile);

                ZipFileInfo fileInfo = new ZipFileInfo(
                    file,
                    this.currentArchiveNumber,
                    attributes,
                    lastWriteTime,
                    fileStream.Length,
                    0,
                    compressionMethod);

                bool          zip64      = forceZip64 || fileStream.Length >= (long)UInt32.MaxValue;
                ZipFileHeader fileHeader = new ZipFileHeader(fileInfo, zip64);

                this.CheckArchiveWriteStream(
                    streamContext,
                    maxArchiveSize,
                    fileHeader.GetSize(false),
                    ref archiveStream);

                long headerPosition = archiveStream.Position;
                fileHeader.Write(archiveStream, false);
                headerArchiveNumber = this.currentArchiveNumber;

                uint crc;
                long bytesWritten = this.PackFileBytes(
                    streamContext,
                    fileStream,
                    maxArchiveSize,
                    compressionStreamCreator,
                    ref archiveStream,
                    out crc);

                fileHeader.Update(
                    bytesWritten,
                    fileStream.Length,
                    crc,
                    headerPosition,
                    headerArchiveNumber);

                streamContext.CloseFileReadStream(file, fileStream);
                fileStream = null;

                // Go back and rewrite the updated file header.
                if (this.currentArchiveNumber == headerArchiveNumber)
                {
                    long fileEndPosition = archiveStream.Position;
                    archiveStream.Seek(headerPosition, SeekOrigin.Begin);
                    fileHeader.Write(archiveStream, false);
                    archiveStream.Seek(fileEndPosition, SeekOrigin.Begin);
                }
                else
                {
                    // The file spanned archives, so temporarily reopen
                    // the archive where it started.
                    string headerArchiveName = streamContext.GetArchiveName(
                        headerArchiveNumber + 1);
                    Stream headerStream = null;
                    try
                    {
                        headerStream = streamContext.OpenArchiveWriteStream(
                            headerArchiveNumber, headerArchiveName, false, this);
                        headerStream.Seek(headerPosition, SeekOrigin.Begin);
                        fileHeader.Write(headerStream, false);
                    }
                    finally
                    {
                        if (headerStream != null)
                        {
                            streamContext.CloseArchiveWriteStream(
                                headerArchiveNumber, headerArchiveName, headerStream);
                        }
                    }
                }

                this.OnProgress(ArchiveProgressType.FinishFile);

                return(fileHeader);
            }
            finally
            {
                if (fileStream != null)
                {
                    streamContext.CloseFileReadStream(
                        this.currentFileName, fileStream);
                }
            }
        }