Build a C# .NET Zip Backup Utility
by Peter A. Bromberg, Ph.D.

Peter Bromberg

Zip Backups have become almost ubiquitous, but the various zip and rar utilities often don't have all the command line switches we need. For example the Winzip command line utility (which by itself is an extra add-on install) does not have a switch to exclude whole subfolders, only one to exclude files. So if you want to make scheduled backups of your web application tree, what do you do? Well, one answer is, "roll your own" ! Thanks to Mike Krueger and his crew, we have the ICSharpCode.ZipLib library which is 100% native .NET Framework code.



Because of Mike's excellent work, putting together a console app that can be run via a batch file or by Task Scheduler to make regular unattended, dated backup files is not too difficult. I put this one together because I needed it, the code is yours to keep and improve in any way you see fit. Let's breeze through the console app code below, and then I will explain various points:

using ICSharpCode.SharpZipLib.Zip;
using System;
using System.Collections;
using System.Collections.Specialized ;
using System.IO;
namespace BackIt { class Backup { static Hashtable excludeDirs = new Hashtable();
static string startFolder = String.Empty;
static string outputZipPath = String.Empty;
static string password = String.Empty;
static NameValueCollection configItems ;
[STAThread] static void Main(string[] args) { configItems = System.Configuration.ConfigurationSettings.AppSettings;
string[] dirs = configItems["excludeDirs"].ToString().Split('|');
foreach(string dir in dirs) excludeDirs.Add(dir.ToUpper(),dir.ToUpper());
Backup.startFolder = configItems["startFolderPath"].ToString();
Backup.outputZipPath = configItems["outputZipPath"].ToString();
Backup.password = configItems["password"].ToString();
ZipIt(Backup.startFolder,Backup.outputZipPath + "\\" + DateTime.Now.ToString("yyyyddmmhhmm") + ".zip", Backup.password);
} public static void ZipIt( string Path, string outPathAndZipFile, string password) { string OutPath = outPathAndZipFile;
ArrayList ar = GenerateFileList(Path);
// generate file list // find number of chars to remove from orginal file path int TrimLength = (Directory.GetParent(Path)).ToString().Length;
TrimLength += 1;
//remove '\' FileStream ostream;
byte[] obuffer;
ZipOutputStream oZipStream = new ZipOutputStream(System.IO.File.Create(OutPath));
// create zip stream if(password != String.Empty)oZipStream.Password = password;
oZipStream.SetLevel(9);
// 9 = maximum compression level ZipEntry oZipEntry;
foreach(string Fil in ar) // for each file, generate a zipentry { oZipEntry = new ZipEntry(Fil.Remove(0,TrimLength));
oZipStream.PutNextEntry(oZipEntry);
if( ! Fil.EndsWith(@"/")) // if a file ends with '/' its a directory { ostream = File.OpenRead(Fil);
obuffer = new byte[ostream.Length];
// byte buffer ostream.Read(obuffer,0,obuffer.Length);
oZipStream.Write(obuffer,0,obuffer.Length);
Console.Write(".");
} } oZipStream.Finish();
oZipStream.Close();
} private static ArrayList GenerateFileList(string Dir) { ArrayList mid = new ArrayList();
bool Empty = true;
foreach(string file in Directory.GetFiles(Dir)) // add each file in directory { mid.Add(file);
Empty = false;
} if(Empty) { if(Directory.GetDirectories(Dir).Length == 0) // if directory is completely empty, add it { mid.Add(Dir + @"/");
} } foreach(string dirs in Directory.GetDirectories(Dir)) // do this recursively { // set up the excludeDir test string testDir = dirs.Substring(dirs.LastIndexOf(@"\") + 1).ToUpper();
if(Backup.excludeDirs.Contains(testDir)) continue;
foreach(object obj in GenerateFileList(dirs)) { mid.Add(obj);
} } return mid;
// return file list } } }

And here is the App.Config file contents, just to bring everything into place:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<!-- if zip password is desired, fill one in-->
<add key="password" value="mypass" />
<!--Starting folder from which to recursively zip all folders and files, with no trailing slash -->
<add key="startFolderPath" value="C:\inetpub\wwwroot\yoursitefolder" />
<!--Pipe-delimited list of folder names to exclude from the zip process-->
<add key="excludeDirs" value="unwanteddir|otherunwanteddir" />
<!--output folder for finished zip file, no trailing slash-->
<add key="outputZipPath" value="C:\backups" />
</appSettings>
</configuration>

Let's breeze through this:

First, the app reads the appSettings into a NameValueCollection (that's what appSettings is) for ease of work. It assigns the various values to static variables, and parses the excluded files list into a string array, then inserts the items into a Hashtable so we can use its handy "Contains" method later.

Then the main "ZipIt" method creates the required ZipOutputStream for use by the SharpZipLib library. Finally it iterates over the folder, subfolders and all files to create the correct listing of files and paths for the zip file, and each is read into the oZipStream zip input stream. In the last part of the code, we test to see if a folder is in the excluded directories Hashtable, and if so, we skip it:

string testDir = dirs.Substring(dirs.LastIndexOf(@"\") + 1).ToUpper();
if(Backup.excludeDirs.Contains(testDir)) continue;

The result is a very clean, efficient way to create named zip file backups with the current DateTime appended. You can run this from Task Scheduler, a batch file, or you can even run it from an ASP.NET page! I've compiled all the required SharpZipLib classes into the executable so there is only one file, plus the config file. Enjoy!

Download the complete VS.NET 2003 solution below

 

 


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.

Article Discussion: