Thanks for the feedback on the memory-usage, I've downloaded
the source for SharpZipLib and am researching a correction
for ZipTask.cs so that the each file won't have to be loaded
into memory.  Just so people will know, that's how it is
implemented currently in the CVS archive.

In the meantime, I discovered that I accidentally sent off
an older version of the zipfileset implementation for [ZipTask.cs].
The correct one is attached.  Basically, the older version limited
zipfilesets to include a maximum of 5 files (simple counter to
break a foreach loop), which I used to debug the task on larger
directories.

Enjoy!

-Wally

------------------------------------------------------------------------
// NAnt - A .NET build tool
// Copyright (C) 2001 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Mike Krueger ([EMAIL PROTECTED])
// Gerry Shaw ([EMAIL PROTECTED])

using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Xml;
using SourceForge.NAnt.Attributes;

using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Zip;

namespace SourceForge.NAnt.Tasks {

     /// <summary>
     /// A task to create a zip file from a specified fileset.
     /// </summary>
     /// <remarks>
     ///   <para>Uses <a 
href="http://www.icsharpcode.net/OpenSource/NZipLib/";>NZipLib</a>, an open 
source Zip/GZip library written entirely in C#.</para>
     /// </remarks>
     /// <example>
     ///   <para>Zip all files in the subdirectory <c>build</c> to 
<c>backup.zip</c>.</para>
     ///   <code>
     ///     <![CDATA[
     /// <zip zipfile="backup.zip">
     ///     <fileset basedir="build">
     ///         <includes name="*.*"/>
     ///     </fileset>
     /// </zip>
     ///     ]]>
     ///   </code>
     /// </example>
     [TaskName("zip")]
     public class ZipTask : Task {
         string _zipfile = null;
         int _ziplevel = 6;
         FileSet _fileset = new FileSet();
         Crc32 crc = new Crc32();
                ArrayList _zipFileSetGroup=new ArrayList();

         /// <summary>The zip file to create.</summary>
         [TaskAttribute("zipfile", Required=true)]
         public string ZipFileName { get { return 
Project.GetFullPath(_zipfile); } set {_zipfile = value; } }

         /// <summary>Desired level of compression (default is 6).</summary>
         /// <value>0 - 9 (0 - STORE only, 1-9 DEFLATE (1-lowest, 
9-highest))</value>
         [TaskAttribute("ziplevel", Required=false)]
         public int    ZipLevel    { get { return _ziplevel; } set 
{_ziplevel = value; } }

         /// <summary>The set of files to be included in the archive.</summary>
         [FileSet("fileset")]
         public FileSet ZipFileSet { get { return _fileset; } }

         protected override void ExecuteTask() {
             ZipOutputStream zOutstream = new 
ZipOutputStream(File.Create(ZipFileName));

                  // set the compression level
                  int zipLevel = ZipLevel;
                  if (zipLevel > 0) {
                     zOutstream.SetLevel(zipLevel);
                  }
                  else {
                     zOutstream.SetMethod(ZipOutputStream.STORED);
                  }

                  AppendFilesFromFileSetToZipArchive(ref zOutstream);
                  AppendFilesFromZipFileSetsToZipArchive(ref zOutstream);

                  zOutstream.Finish();
                  zOutstream.Close();
            }

            private String RemoveLeadingPath(string leadPath,string path) {
                    if(null==path) return null; // avoid 'null' scenarios up-front

                    if(path.StartsWith(leadPath)) {
                            path=path.Substring(leadPath.Length);
                    }

                    // we strip any leading seperator char's as they tend to arise
                    // inappropriately for network paths, not sure if this is a 
'feature'
                    // or a special case of the Path.GetPathRoot functionality
                    while(path[0]==Path.DirectorySeparatorChar) {
                            path=path.Substring(1);
                    }
                    return path;

            }
        
            /// <summary>
            /// Replace the leading part of the full file path with the prefix.
            /// we spend effort to remove leading directory seperators from the 
'fn' so that
            /// the zip archive doesn't contain any of them in the leading position
            /// For example, we want the archive to have
            ///    dir123\file.dat
            /// Not
            ///    \dir123\file.dat
            /// </summary>
            /// <param name="startDir"></param>
            /// <param name="prefix"></param>
            /// <param name="fn"></param>
            /// <returns></returns>
            private String ReplaceFullFilePathWithPrefix(string startDir, string 
prefix, string fn)
            {
                    string rootPath=Path.GetPathRoot(fn);

                    // strip rootPath from startDir & fn
                    startDir=RemoveLeadingPath(rootPath,startDir);
                    fn=RemoveLeadingPath(rootPath,fn);

                    if(0<prefix.Length) {
                            fn=RemoveLeadingPath(startDir,fn);          
                            return Path.Combine(prefix,fn);
                    }
                    return fn;
            }

            private void AppendFilesFromFileSetToZipArchive(ref ZipOutputStream 
zOutstream) {
                    if(Verbose) {
                            int fileCount=ZipFileSet.FileNames.Count;
                            string fileText=(1==fileCount) ? "file" : "files";
                            Log.WriteLine(LogPrefix + "Zipping {0} {1} to {2}", 
fileCount, 
fileText, ZipFileName);
                    }

                    string basePath = 
Path.GetDirectoryName(Project.GetFullPath(ZipFileSet.BaseDirectory));

                    foreach (string file in ZipFileSet.FileNames) {
                            string entryName = file.Substring(basePath.Length + 1);
                            ZipFileUp(ref zOutstream, file, entryName);
                    }
            }

            private void AppendFilesFromZipFileSetsToZipArchive(ref 
ZipOutputStream zOutstream) {
                    foreach(ZipFileSetElement zfs in _zipFileSetGroup) {
                            string 
filePath=Path.GetDirectoryName(Project.GetFullPath(zfs.Dir+Path.DirectorySeparatorChar));

                            FileSet fs=new FileSet();
                            fs.BaseDirectory=filePath;
                            fs.Includes.Add(zfs.IncludesPattern);
                            fs.DefaultExcludes=false;
                            fs.Excludes.Add(zfs.ExcludesPattern);

                            if(Verbose) {
                                    int fileCount=fs.FileNames.Count;
                                    string fileText=(1==fileCount) ? "file" : "files";
                                    Log.WriteLine(LogPrefix + "Zipping {0} {1} to 
{2}", fileCount, 
fileText, ZipFileName);
                            }

                            foreach(String fn in fs.FileNames) {
                                    string 
zipEntryName=ReplaceFullFilePathWithPrefix(fs.BaseDirectory,zfs.Prefix,fn);
                                    ZipFileUp(ref zOutstream, fn, zipEntryName);
                            }
                    }
            }

            /// <summary>
            /// NOTE: Since we are reading in the entire file into memory and then 
compressing it,
            /// we will be incurring a memory penalty (especially in terms of 
memory allocation/
            /// garbage collection). What would be better is to pass the data 
stream to
            /// the NZipLib, however, currently that library doesn't handle 
this.  Their is also
            /// a performance limitation on larger files, which have to be loaded 
into memory all
            /// at once.
            /// NOTE: Would it make sense to research some sort of mechanism to 
zip-up files that
            /// are currently locked by applications?  For example, my need is to 
have the Visual
            /// Studio project to be archived, I don't want to quit my project to 
be able to zip
            /// things up.
            /// </summary>
            /// <param name="zOutstream"></param>
            /// <param name="fnOnDisk"></param>
            /// <param name="zipEntryName"></param>
            private void ZipFileUp(ref ZipOutputStream zOutstream, string 
fnOnDisk, string zipEntryName) {
                    if (!File.Exists(fnOnDisk)) {
                            throw new FileNotFoundException();
                    }
                
                    // read source file
                    FileStream fStream = File.OpenRead(fnOnDisk);

                    long   fileSize = fStream.Length;
                    byte[] buffer = new byte[fileSize];
                    fStream.Read(buffer, 0, buffer.Length);
                    fStream.Close();

                    // create ZIP entry
                    ZipEntry entry = new ZipEntry(zipEntryName);
                    if (Verbose) {
                            Log.WriteLine(LogPrefix + " Adding {0}", zipEntryName);
                    }

                    if (ZipLevel == 0) {
                            entry.Size = fileSize;

                            // calculate crc32 of current file
                            crc.Reset();
                            crc.Update(buffer);
                            entry.Crc  = crc.Value;
                    }

                    // write file to ZIP
                    zOutstream.PutNextEntry(entry);
                    zOutstream.Write(buffer, 0, buffer.Length);
            }

            protected override void InitializeElement(XmlNode elementNode) {
                    // The ZipFileSet class will initialize the marked xml attributes 
but
                    // not the unmarked sub-elements.  We don't have any for 
ZipFileSet yet,
                    // but they could go here.  Alternatively we could have built the
                    // group-of-ZipFileSet from an overridden InitializeTask.
                    foreach (XmlNode node in elementNode) {
                            if("zipfileset".Equals(node.Name)) {
                                    ZipFileSetElement zfs = new ZipFileSetElement();
                                    zfs.Project=Project;
                                    zfs.Initialize(node);

                                    _zipFileSetGroup.Add(zfs);
                            }
                    }
            }

            [ElementName("zipfileset")]
                    private class ZipFileSetElement : Element
            {
                    private string _dir="";
                    private string _prefix="";
                    private string _includesPattern="";
                    private string _excludesPattern="";

                    [TaskAttribute("prefix", Required=false)]
                    public string Prefix
                    {
                            get { return _prefix; }
                            set { _prefix=value;}
                    }

                    [TaskAttribute("dir", Required=false)]
                    public string Dir
                    {
                            get { return _dir; }
                            set { _dir=value; }
                    }

                    [TaskAttribute("includes", Required=false)]
                    public string IncludesPattern
                    {
                            get { return _includesPattern; }
                            set { _includesPattern=value;}
                    }

                    [TaskAttribute("excludes", Required=false)]
                    public string ExcludesPattern
                    {
                            get { return _excludesPattern; }
                            set { _excludesPattern=value;}
                    }
            }
     }
}



-------------------------------------------------------
This sf.net email is sponsored by: Dice - The leading online job board
for high-tech professionals. Search and apply for tech jobs today!
http://seeker.dice.com/seeker.epl?rel_code=31
_______________________________________________
Nant-developers mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/nant-developers

Reply via email to