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