Introduction
"File system browsing? What the heck is that?"
"Old way? New way? is there another way???"
I guess most of us are still on the pre-.NET mindset, the term I commonly use when people think of solutions that are based on the ways of doing stuff without utilizing the benefits of the new technology which could help in making the task a lot easier. A great example of this type of mindset is this, what comes to your mind when you are asked to create a Web Form that enables the user to list the contents of a folder and enables the user to download selected files on that folder? I bet you more than 80% of the programmers would have this answer.
"Yeah, I can build a function that reads the folder and write it like what I used to do on ASP 3.0 where I build the layout using HTML tags and just loop thru all the directories and files inside the specified folder. I would then use the Response.Write
method to write the structure of the table to an HTML page."
Guilty??
I guess most of us still think this way...
What if I tell you that you can do this task by just using some System.IO
objects, a DataTable
and a DataGrid
? Strange huh? But it's true! Before we proceed to the solution, I want to show you an example of a code written by an ASP.NET programmer that uses the old way of accomplishing this task:
Here is the C# example of the classic code:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Text;
using System.IO;
using System.Configuration;
namespace KeithRull.FolderBrowserCS
{
public class theOldWayBrowser : System.Web.UI.Page
{
protected System.Web.UI.WebControls.PlaceHolder plhFiles;
private void Page_Load(object sender, System.EventArgs e)
{
LiteralControl lcTableHead = new
LiteralControl(@"<TABLE id=FileSystem" +
@"style='FONT-SIZE: xx-small;" +
@"BORDER-TOP-STYLE: none;" +
@"FONT-FAMILY: Arial;" +
@"BORDER-RIGHT-STYLE: none;" +
@"BORDER-LEFT-STYLE: none;" +
@"BORDER-COLLAPSE: collapse;" +
@"BORDER-BOTTOM-STYLE: none'" +
@"cellSpacing=0" +
@"rules=all border=1>");
plhFiles.Controls.Add(lcTableHead);
LiteralControl lcContents;
LiteralControl lcTableHeader = new
LiteralControl(@"<TR>" +
@"<TD style='WIDTH: 80px'>Type</TD>" +
@"<TD style='WIDTH: 350px'>Name</TD>" +
@"<TD style='WIDTH: 150px'>CreationTime</TD>" +
@"<TD style='WIDTH: 150px'>LastWriteTime</TD>" +
@"</TR>");
plhFiles.Controls.Add(lcTableHeader);
try
{
string strDir = Request.QueryString["d"] == null ?
FileBrowerProperty.IntialPath : Request.QueryString["d"];
DirectoryInfo DirInfo = new DirectoryInfo(strDir);
DirectoryInfo[] subDirs = DirInfo.GetDirectories();
FileInfo[] Files = DirInfo.GetFiles();
if((Files.Length != 0) && (subDirs.Length!=0))
{
foreach(DirectoryInfo di in subDirs)
{
LiteralControl lcFolders =
new LiteralControl(@"<TR>" +
@"<TD style='WIDTH: 80px'>" +
@"Directory</TD>" +
@"<TD style='WIDTH: 350px'>" +
@"<a href='theOldWayBrowser.aspx?d=" +
@di.FullName + "'>" + di.Name +
"</a></TD><TD style='WIDTH: 150px'>" +
di.CreationTime +
"</TD><TD style='WIDTH: 150px'>" +
di.LastWriteTime + "</TD></TR>");
plhFiles.Controls.Add(lcFolders);
}
foreach(FileInfo fi in Files)
{
LiteralControl lcContentsHead =
new LiteralControl("<tr><td>File</td><td>");
plhFiles.Controls.Add(lcContentsHead);
LinkButton lb = new LinkButton();
lb.Text = @fi.Name;
lb.CommandName = @fi.Name;
lb.CommandArgument = @fi.FullName;
lb.Click += new EventHandler(Download);
plhFiles.Controls.Add(lb);
LiteralControl lcContentsTail =
new LiteralControl("</td><td>" +
fi.CreationTime +
"</td><td valign='bottom'>"+
fi.LastWriteTime+"</td></tr>");
plhFiles.Controls.Add(lcContentsTail);
}
}
else
{
lcContents =
new LiteralControl("<tr>" +
"<td colspan = 4>" +
"No file/folder found inside this " +
"directory.</td></tr>");
plhFiles.Controls.Add(lcContents);
}
}
catch(Exception ex)
{
lcContents =
new LiteralControl("<tr>" +
"<td colspan = 4> Error encountered" +
" while trying to parse directory. " +
ex.Message + "</td></tr>");
plhFiles.Controls.Add(lcContents);
}
LiteralControl lcTableTail =
new LiteralControl("</table>");
plhFiles.Controls.Add(lcTableTail);
}
private void Download(object sender, System.EventArgs e)
{
string filepath = ((LinkButton) sender).CommandArgument;
string filename = ((LinkButton) sender).CommandName;
Stream s = File.OpenRead(filepath);
Byte[] buffer = new Byte[s.Length];
try
{
s.Read(buffer, 0, (Int32) s.Length);
}
finally { s.Close(); }
Response.ClearHeaders();
Response.ClearContent();
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition",
"attachment; filename=" + filename);
Response.BinaryWrite(buffer);
Response.End();
}
}
}
Doesn't this look like an old ASP implementation? Working with the interface while reading the data?
As I said a while ago, we can accomplish the same task by using DataTable
s and a DataGrid
. First, let's do a little examination of the DataGrid.DataSource
property. DataGrid.DataSource
property accepts any object that implements an IList
interface. With this in mind, let's take a look at the values returned by DirectoryInfo.GetDirectories()
, DirectoryInfo.GetFiles()
and Directory.FileSystemInfo()
.>.
The DirectoryInfo.GetDirectories()
is a method that is used when you want to get a collection of all the subdirectories inside a directory. While the DirectoryInfo.GetFiles()
gets a collection of all the files inside a specified directory. On the other hand, DirectoryInfo.GetFileSystemInfos()
gets a collection of all the files and directories inside a folder. Since these three methods return a collection of files, directories or both, we can safely assume that we can use the result set returned by these objects as the data source of our DataGrid
.
The listing below shows how to list the contents of a folder using DirectoryInfo.FileSystemInfo()
:
private void Page_Load(object sender, System.EventArgs e)
{
string folderToBrowse = @"c:\";
DirectoryInfo DirInfo =
new DirectoryInfo(folderToBrowse);
FileSystemGrid.DataSource =
DirInfo.GetFileSystemInfos();
FileSystemGrid.DataBind();
}
Please take a note that you need to setup your grid to accept the values that are generated by the DirectoryInfo.GetFileSystemInfo
method. A sample template is as follows:
<asp:datagrid id="FileSystemGrid" runat="server"
BorderStyle="None" AutoGenerateColumns="False"
Font-Size="XX-Small" Font-Names="Arial"
AllowSorting="True">
<Columns>
<asp:TemplateColumn HeaderText="Name">
<HeaderStyle Width="350px"></HeaderStyle>
<ItemTemplate>
<asp:HyperLink id=systemLink runat="server" NavigateUrl=''
Text='<%# DataBinder.Eval(Container, "DataItem.FullName") %>'>
</asp:HyperLink>
</ItemTemplate>
</asp:TemplateColumn>
<asp:BoundColumn DataField="CreationTime"
HeaderText="CreationTime">
<HeaderStyle Width="150px"></HeaderStyle>
</asp:BoundColumn>
<asp:BoundColumn DataField="LastWriteTime"
HeaderText="LastWriteTime">
<HeaderStyle Width="150px"></HeaderStyle>
</asp:BoundColumn>
</Columns>
</asp:datagrid>
The result of our first sample is shown below:
This is simple? right?
What if we want to add some user interactions? For example, clicking on a selected directory would allow the user to browse the underlying files and directories. To accomplish this task, we need to have a LinkButton
, our DataTable
and our DataGrid
. But this time, we would be separating the Folder table and the File table. This is necessary because we want to have different interactions. For example, clicking a folder would enable us to browse the sub folder and files on that folder or clicking on the file would enable us to download that file.
Here is the C# example of the solution to this problem:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.IO;
namespace KeithRull.FolderBrowserCS
{
public class theNewWayBrowser : System.Web.UI.Page
{
protected System.Web.UI.WebControls.DataGrid FileSystem;
private void Page_Load(object sender, System.EventArgs e)
{
string folderToBrowse = Request.QueryString["d"] == null ?
FileBrowerProperty.IntialPath : Request.QueryString["d"];
DirectoryInfo DirInfo = new DirectoryInfo(folderToBrowse);
DataTable fileSystemFolderTable = new DataTable();
DataTable fileSystemFileTable = new DataTable();
DataTable fileSystemCombinedTable = new DataTable();
DataColumn dcFileType = new DataColumn("Type");
DataColumn dcFileFullName = new DataColumn("FullName");
DataColumn dcFileName = new DataColumn("Name");
DataColumn dcFileCreationTime = new DataColumn("CreationTime");
DataColumn dcFileLastWriteTime = new DataColumn("LastWriteTime");
DataColumn dcFolderType = new DataColumn("Type");
DataColumn dcFolderFullName = new DataColumn("FullName");
DataColumn dcFolderName = new DataColumn("Name");
DataColumn dcFolderCreationTime = new DataColumn("CreationTime");
DataColumn dcFolderLastWriteTime = new DataColumn("LastWriteTime");
fileSystemFolderTable.Columns.Add(dcFileType);
fileSystemFolderTable.Columns.Add(dcFileName);
fileSystemFolderTable.Columns.Add(dcFileFullName);
fileSystemFolderTable.Columns.Add(dcFileCreationTime);
fileSystemFolderTable.Columns.Add(dcFileLastWriteTime);
fileSystemFileTable.Columns.Add(dcFolderType);
fileSystemFileTable.Columns.Add(dcFolderName);
fileSystemFileTable.Columns.Add(dcFolderFullName);
fileSystemFileTable.Columns.Add(dcFolderCreationTime);
fileSystemFileTable.Columns.Add(dcFolderLastWriteTime);
foreach(DirectoryInfo di in DirInfo.GetDirectories())
{
DataRow fileSystemRow = fileSystemFolderTable.NewRow();
fileSystemRow["Type"] = "Directory";
fileSystemRow["Name"] = di.Name;
fileSystemRow["FullName"] = di.FullName;
fileSystemRow["CreationTime"] = di.CreationTime;
fileSystemRow["LastWriteTime"] = di.LastWriteTime;
fileSystemFolderTable.Rows.Add(fileSystemRow);
}
foreach(FileInfo fi in DirInfo.GetFiles())
{
DataRow fileSystemRow = fileSystemFileTable.NewRow();
fileSystemRow["Type"] = "File";
fileSystemRow["Name"] = fi.Name;
fileSystemRow["FullName"] = fi.FullName;
fileSystemRow["CreationTime"] = fi.CreationTime;
fileSystemRow["LastWriteTime"] = fi.LastWriteTime;
fileSystemFileTable.Rows.Add(fileSystemRow);
}
fileSystemCombinedTable = fileSystemFolderTable.Copy();
foreach(DataRow drw in fileSystemFileTable.Rows)
{
fileSystemCombinedTable.ImportRow(drw);
}
FileSystem.DataSource = fileSystemCombinedTable;
FileSystem.DataBind();
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
InitializeComponent();
base.OnInit(e);
}
private void InitializeComponent()
{
this.FileSystem.ItemCommand +=
new DataGridCommandEventHandler(FileSystem_ItemCommand);
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
private void FileSystem_ItemCommand(object source,
DataGridCommandEventArgs e)
{
string filepath = e.CommandArgument.ToString();
string fileSystemType =
FileSystem.Items[e.Item.ItemIndex].Cells[0].Text;
if(fileSystemType == "Directory")
{
Response.Redirect("theNewWayBrowser.aspx?d="+
e.CommandArgument.ToString());
}
else
{
string filename = e.CommandName;
Stream s = File.OpenRead(filepath);
Byte[] buffer = new Byte[s.Length];
try {
s.Read(buffer, 0, (Int32) s.Length);
}
finally { s.Close(); }
Response.ClearHeaders();
Response.ClearContent();
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition",
"attachment; filename=" + filename);
Response.BinaryWrite(buffer);
Response.End();
}
}
}
}
The screenshot of the result of our solution is shown below:
As you can see, we have accomplished the same result as the first example that uses the legacy ASP logic using native ASP.NET objects.