The main method of this C# console program is pretty straightforward. It starts by examing the command line arguments. These are the possibilities:
-d<depth> : max directory depth to include in report
-s<directory>: directory in which to begin search
-h or -? : help, presents a list of the possible arguments
So if the user enters DirectorySize -d2 -sc:\windows, then the report will start searching in c:\windows and only show directories less that 2 levels below c:\windows (altough all the directories below windows are searched for their sizes.)
After the command line has been parsed, the beginning of the HTML file is written which simply sets up a table. Next, the recursive method is called to start the process. After the program returns from the method, the end of the HTML file is written, the file is closed and the program is finished.
using System; using System.IO; namespace DirectorySize { class DirectorySize { static FileStream fs = new FileStream("report.html", FileMode.Create, FileAccess.Write); static StreamWriter writer = new StreamWriter(fs); static int maxReportDepth = -1; static int currentDepth = 0; static void Main(string[] args) { // set default search location to c: String searchDir = "c:\\"; // parse the command line arguments if (args.Length > 0) { for (int i = 0; i < args.Length; i++) { switch(args[i][1]) { case 'd': // specify max depth for report maxReportDepth = Int32.Parse(args[i].Substring(2)); break; case 's': // beginning search directory // if a directory is specified, we need to make sure it is valid try { searchDir = args[i].Substring(2); Directory.SetCurrentDirectory(searchDir); } catch { Console.WriteLine(args[0] + " is not a valid directory"); } break; case '?': goto case 'h'; case 'h': Console.WriteLine("usage: DirectorySize " + "[-d<depth>] [-s<directory>]"); Console.WriteLine(" -d<depth>: max directory " + "depth to include in report"); Console.WriteLine(" -s<directory>: directory " + "in which to begin search"); return; default: Console.WriteLine(args[i] + " - unrecognized argument"); Console.WriteLine("Use -h for help"); return; } } } // write the beginning of the HTML file // this will be a simple table writer.WriteLine("<html>"); writer.WriteLine("<head>"); writer.WriteLine("<title>DirectorySize Report</title>"); writer.WriteLine("</head>"); writer.WriteLine("<body>"); writer.WriteLine("<font face=\"Courier New\" size=\"2\">"); writer.WriteLine("<table border=\"1\" " + "bordercolor=\"#111111\" " + "width=\"100%\" cellpadding=\"2\" cellspacing=\"0\" " + "style=\"border-collapse: collapse\">"); writer.WriteLine(" <tr>"); writer.WriteLine(" <td align=\"left\">" + "<b>Directory</b></td>"); writer.WriteLine(" <td width=\"100\" " + "align=\"right\"><b>MB</b></td>"); writer.WriteLine(" </tr>"); // make the first call to the recursive searching method analyzeDirectory(searchDir); // close the file off writer.WriteLine("</table>"); writer.WriteLine("</font>"); writer.WriteLine("</body>"); writer.WriteLine("</html>"); writer.Close(); } |
The idea of the recursive method analyzeDirectory() is fairly simple. First
it gets a list of all the immediate subdirectories. Then it calls
analyzeDirectory on each of those subdirectories. After it finishes
anazlying the subdirectories, the method gets a list of all the files in
the directory. The length (in bytes) of each of these files is added to a
running sum. The sum is then convereted to megabytes (by dividing by 1024
twice) and a new row is printed for the table. Finally, the size of the
subdirectories plus the size of the files in this directory is returned to
the calling method. This completes the recursion.
There is also a
variable called currentDepth. This is incremented when a subdirectory is
entered and decremented when a directory is left. The point of this is to
make sure that only the directory level that the user specified (with the
-d option) is included in the report.
static long analyzeDirectory(string path) { // first recurse into all subdirectories long sizeOfDirectories = 0; try { String[] subdirectories = Directory.GetDirectories(path); foreach (String current in subdirectories) { // moving down one level in the directory structure currentDepth++; sizeOfDirectories += analyzeDirectory(current); } } catch { // most likely a security error // i.e. the current user doesn't have access to this directory Console.WriteLine(path + " Access denied."); currentDepth--; return 0; } // once the subdirectories are searched, get the total // size (int bytes) of all files in this path long fileBytes = 0; foreach (String current in Directory.GetFiles(path)) { FileInfo info = new FileInfo(current); fileBytes += info.Length; } // now write a line indicating the size of the directories added // to the size of the files in that directory if this level is // included in the report if (currentDepth <= maxReportDepth || maxReportDepth == -1) { writer.WriteLine(" <tr>"); writer.WriteLine(" <td align=\"left\">" + path + "</td>"); double total = (double)(sizeOfDirectories + fileBytes)/(1024.0*1024.0); writer.WriteLine(" <td width=\"100\" align=\"right\">" + total.ToString("n") + "</td>"); writer.WriteLine(" </tr>"); } // moving up one level in the directory structure currentDepth--; return sizeOfDirectories + fileBytes; } } } |
That's all there is to it. This program obviously takes a couple minutes to
run as it has to touch every file on your system. However, a few minutes
of CPU chunking is preferrable to manually looking through directories!
This code was accepted as a tutorial on Devhood!
Disclaimer:
By installing this software, you assume complete responsibility for any
damage that may occur to your system or your oldest child. I assume no
liability. Besides, if you tried to sue me you wouldn't get much.
May cause cancer in pregnant
lab rats on rainy days in Arkansas. Violators will be persecuted. Do not
pass Go, do not collect $200, go directly to jail. An apple a day keeps the
doctor away. Caffeine-free (although I am probably not). Look both ways
before crossing the road. Don't put that quarter in your mouth, you don't
know where it's been. Do not use in areas of high radiation. Do not use
while being chased by a rabid African tiger (a healthy one maybe, but not a
rabid one). I am not responsible for any lost or stolen brain cells. And
last, but not least, have fun!