Enumerating Drives
The first thing we should find out is which drives actually are available for browsing on the computer (listdrives.aspx):
<% @Page Language="C#" %>
<% @Import Namespace="System.IO" %>
<%
string[] achDrives = Directory.GetLogicalDrives();
int nNumOfDrives = achDrives.Length;
Response.Write("<ul>");
for (int i=0; i < nNumOfDrives; i++)
{
Response.Write("<li><a href=\"listdir.aspx?dir=");
Response.Write(Server.UrlEncode(achDrives[i]));
Response.Write("\">" + achDrives[i]);
Response.Write("</a><br>");
}
Response.Write("</ul>");
%>
In the System.IO namespace (bound in with the @Import Namespace instruction) there is a class called Directory. This represents functionalities that can be used with directories and this class also contains the static (callable without object instance) method GetLogicalDrives. This method gives us an array of strings containing the drive letters.Of course I didn't leave it at a simple listing of the drives obtained through GetLogicalDrives - I added a link to a further page which gives information about the contents of the root directory of the drive.
Listing Directories and Files
We are in a directory now. What is of interest for us there? For example the subdirectories and files as well as the properties of the directory itself (date of creation etc.). The following source code listing of the file listdir.aspx demonstrates how the required functionality might be implemented:
<% @Page Language="C#" %>
<% @Import Namespace="System.IO" %>
<%
string strDir2List = Request.QueryString.Get("dir");
DirectoryInfo thisOne = null;
try
{
thisOne = new DirectoryInfo(strDir2List);
// Reading the directory properties
Response.Write("<p>Creation: " +
thisOne.CreationTime.ToString() + "</p>");
DirectoryInfo[] subDirectories = thisOne.GetDirectories();
Response.Write("<ul>");
for (int i=0; i < subDirectories.Length; i++)
{
Response.Write("<li><a href=\"listdir.aspx?dir=");
Response.Write(Server.UrlEncode(subDirectories[i].FullName));
Response.Write("\">" + subDirectories[i].Name);
Response.Write("</a><br>");
}
Response.Write("</ul>");
FileInfo[] theFiles = thisOne.GetFiles();
Response.Write("<ul>");
for (int i=0; i < theFiles.Length; i++)
{
Response.Write("<li><a href=\"showfile.aspx?file=");
Response.Write(Server.UrlEncode(theFiles[i].FullName));
Response.Write("\">" + theFiles[i].Name);
Response.Write("</a><br>");
}
Response.Write("</ul>");
}
catch (Exception e)
{
Response.Write("Access not possible, error: <i>");
Response.Write(e.ToString() + "</i>");
Response.End();
}
%>
Reading the directories works with the GetDirectories method, which returns an array of DirectoryInfo objects to me, with which I can then build the desired functionality (e.g. like here for another link for the Drill-Down). The same applies to the GetFiles method, which returns an array of FileInfo objects. More on this in the next section.For what, please, do I need exception handling? Well, it might be that I try to access something which I am not allowed to access and then I get one across my knuckles (this case might also be well known from Real Life...). Seriously: for example, the NTFS permissions for the current user might not be sufficient (listing forbidden).
Displaying File Information
Now we are at file level. Of course, a file has many properties and the following example showfile.aspx presents just a subset of them. A glance at the documentation reveals the remainder of what I have left out to shorten the script.
<% @Page Language="C#" %>
<% @Import Namespace="System.IO" %>
<html>
<head><title>File Info</title></head>
<body>
<%
string strFile2Show = Request.QueryString.Get("file");
FileInfo thisOne = new FileInfo(strFile2Show);
%>
<table>
<tr><td>Name:</td><td><%=thisOne.Name%></td></tr>
<tr><td>Path:</td><td><%=thisOne.FullName%></td></tr>
<tr><td>Directory:</td><td><%=thisOne.DirectoryName%></td></tr>
<tr>
<td>Date created:</td>
<td><%=thisOne.CreationTime.ToString()%></td>
</tr>
<tr>
<td>Size:</td>
<td><%=thisOne.Length.ToString()%> Bytes</td>
</tr>
<tr>
<td>Last access:</td>
<td><%=thisOne.LastAccessTime.ToString()%></td>
</tr>
<tr>
<td>Last modified:</td>
<td><%=thisOne.LastWriteTime.ToString()%></td>
</tr>
</table>
<%
StreamReader theReader = thisOne.OpenText();
char[] theBuffer = new char[255];
int nRead = theReader.ReadBlock(theBuffer, 0, 255);
Response.Write("<pre>");
Response.Write(Server.HtmlEncode(new String(theBuffer,0,nRead)));
Response.Write("</pre>");
%>
</body>
</html>
At the end of the script I undertook a quick detour to file reading to demonstrate how simple it is. First I open a StreamReader object, create a buffer, fill the buffer with the ReadBlock method and pass the HTML-encoded block over to the client. And presto, a tiny 'File Preview'!. Nothing earth shattering, but it already shows how simple working with the contents of files is.Recursive Listing of Directories
At last, I have a program for creating a directory tree in mind. As the heading already indicates, the tree will be generated recursively - thus we need a function which can be called recursively. In the example recursivelisting.aspx it is called RecursiveDump:
<% @Page Language="C#" %>
<% @Import Namespace="System.IO" %>
<%
string strDir2List = Request.QueryString.Get("dir");
DirectoryInfo thisOne = new DirectoryInfo(strDir2List);
Response.Write("<pre>");
RecursiveDump(thisOne, 0);
Response.Write("</pre>");
%>
<script language="C#" runat="server">
void RecursiveDump(DirectoryInfo theDir, int nLevel)
{
DirectoryInfo[] subDirectories = theDir.GetDirectories();
for (int i=0; i < subDirectories.Length; i++)
{
Response.Write(new String(' ',nLevel));
Response.Write(subDirectories[i].Name + "\r\n");
RecursiveDump(subDirectories[i], nLevel+1);
}
}
</script>
As usual I have made life easier for myself and just use blank spaces for generating the tree. To make that visible in HTML, we have to use a <PRE> Tag - otherwise the blanks will be gobbled up.The function itself has two parameters: a DirectoryInfo object, and an integer variable which denotes the current level. For the current level, the subdirectories are read (with GetDirectories) and then displayed in a for loop. For each DirectoryInfo object the function RecursiveDump is called, and level is increased by one.
In this article, I presented two objects out of the System.IO namespace: Directory and File. Though I used them 'only' for displaying simple directory and file information, these two objects are the base for file processing such as reading and writing. More on this in a future article.