*NOTE: the source code for this projerct in C# and in VB.NET can be found at the end of this post. I have also posted this example in codeproject
"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, a 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 alot more easier. A great example of this type of mindset is this, what comes to your mind when you asked to create a Web form that enables the user to list the contents of a folder and enable the user to download selected files on that folder? I bet you more than 80% of us programmer would have this type 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 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 alot 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 its true! but before we proceed to the solution, i want to show you first an example of a code of an ASP.NET programmer using the old way in 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{ /// <summary> /// Summary description for FolderBrowser. /// </summary> public class theOldWayBrowser : System.Web.UI.Page { protected System.Web.UI.WebControls.PlaceHolder plhFiles; private void Page_Load(object sender, System.EventArgs e) { //create the our table 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; //creathe the column headers 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 { //read our query string, if its null, assign the default folder //to our variable else assign the query string value. string strDir = Request.QueryString["d"] == null ? FileBrowerProperty.IntialPath : Request.QueryString["d"]; //read the directory DirectoryInfo DirInfo = new DirectoryInfo(strDir); //read the subdirectories inside the parent directory DirectoryInfo[] subDirs = DirInfo.GetDirectories(); //read the files in the parent directory FileInfo[] Files = DirInfo.GetFiles(); //check if there are directories and files inside our parent directory if((Files.Length != 0) && (subDirs.Length!=0)) { //loop thru each of the directory inside the parent directory foreach(DirectoryInfo di in subDirs) { //add the directory info to our table 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); } //loop thru each of the file inside the parent directory foreach(FileInfo fi in Files) { //add the file info to our table LiteralControl lcContentsHead = new LiteralControl("<tr><td>File</td><td>"); plhFiles.Controls.Add(lcContentsHead); LinkButton lb = new LinkButton(); //set the label and command name to the filename of the file lb.Text = @fi.Name; lb.CommandName = @fi.Name; //set the command argument to the fullpath lb.CommandArgument = @fi.FullName; //add our handler 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 { // there is file or folder inside the directory lcContents = new LiteralControl("<tr><td colspan = 4> No file/folder found inside this directory.</td></tr>"); plhFiles.Controls.Add(lcContents); } } catch(Exception ex) { //error trap 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) { //get the file path string filepath = ((LinkButton) sender).CommandArgument; //geth the filename string filename = ((LinkButton) sender).CommandName; //read the file to our stream Stream s = File.OpenRead(filepath); //create the bytes to be streamed Byte[] buffer = new Byte[s.Length]; //build the buffer try { s.Read(buffer, 0, (Int32) s.Length); } //close our stream finally { s.Close(); } //clear the response headers Response.ClearHeaders(); //clear the content type Response.ClearContent(); Response.ContentType = "application/octet-stream"; //add our header Response.AddHeader("Content-Disposition", "attachment; filename=" + filename); //write the buffer to the http stream Response.BinaryWrite(buffer); //end response Response.End(); } }}
Doesn't this look like an old ASP implementation? working with the interface while reading the data?
As i have said awhile ago, we can accomplish the same task by using DataTables and a DataGrid. First, lets have a little examination on DataGrid.DataSource property. DataGrid.DataSource property accepts any object that implements an IList interface. With this in mind, Lets take a look at what are the values returned by the DirectoryInfo.GetDirectories(), the DirectoryInfo.GetFiles() and Directory.FileSystemInfo().
The DirectoryInfo.GetDirectories() is a method that is used when you want to get a collection 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 a collection of all the files and directories inside a folder. Since these three methods return a colllection of files, directory or both, we could safely assume that we could use the result set returned by these objects to be the datasource 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 note that you need to setup your grid to accept the values are generated by the DirectoryInfo.GetFileSystemInfo Method. A sample template your 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 interaction? for example clicking on a selected directory would lead me to browse the underlying files and directories. To accomplish that 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 enables 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{ /// <summary> /// Summary description for theNewWayBrowser. /// </summary> public class theNewWayBrowser : System.Web.UI.Page { protected System.Web.UI.WebControls.DataGrid FileSystem; private void Page_Load(object sender, System.EventArgs e) { //read our query string, if its null, assign the default folder //to our variable else assign the query string value. string folderToBrowse = Request.QueryString["d"] == null ? FileBrowerProperty.IntialPath : Request.QueryString["d"]; //read the folder DirectoryInfo DirInfo = new DirectoryInfo(folderToBrowse); //create our datatable that would hold the list //of folders in the specified directory DataTable fileSystemFolderTable = new DataTable(); //create our datatable that would hold the list //of files in the specified directory DataTable fileSystemFileTable = new DataTable(); //create our datatable that would hold the list //of files and folders when we combine the two previously declared datatable DataTable fileSystemCombinedTable = new DataTable(); //create the columns for our file 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"); //create the columns for our folder datatable 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"); //add the columns to our datatable 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); //loop thru each directoryinfo object in the specified directory foreach(DirectoryInfo di in DirInfo.GetDirectories()) { //create a new row in ould folder table DataRow fileSystemRow = fileSystemFolderTable.NewRow(); //assign the values to our table members fileSystemRow["Type"] = "Directory"; fileSystemRow["Name"] = di.Name; fileSystemRow["FullName"] = di.FullName; fileSystemRow["CreationTime"] = di.CreationTime; fileSystemRow["LastWriteTime"] = di.LastWriteTime; fileSystemFolderTable.Rows.Add(fileSystemRow); } //loop thru each fileinfo object in the specified directory foreach(FileInfo fi in DirInfo.GetFiles()) { //create a new row in ould folder table DataRow fileSystemRow = fileSystemFileTable.NewRow(); //assign the values to our table members fileSystemRow["Type"] = "File"; fileSystemRow["Name"] = fi.Name; fileSystemRow["FullName"] = fi.FullName; fileSystemRow["CreationTime"] = fi.CreationTime; fileSystemRow["LastWriteTime"] = fi.LastWriteTime; fileSystemFileTable.Rows.Add(fileSystemRow); } //copy the folder table to our main datatable, //this is necessary so that the parent table would have the //schema of our child tables. fileSystemCombinedTable = fileSystemFolderTable.Copy(); //loop thru each row of our file table foreach(DataRow drw in fileSystemFileTable.Rows) { //import the rows from our child table to the parent table fileSystemCombinedTable.ImportRow(drw); } //assign our file system parent table to our grid and bind it. FileSystem.DataSource = fileSystemCombinedTable; FileSystem.DataBind(); } #region Web Form Designer generated code override protected void OnInit(EventArgs e) { // // CODEGEN: This call is required by the ASP.NET Web Form Designer. // InitializeComponent(); base.OnInit(e); } /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> 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) { //get the filepath from the specified command arguments for our linkbutton string filepath = e.CommandArgument.ToString(); //get the file system type of the selected ite string fileSystemType = FileSystem.Items[e.Item.ItemIndex].Cells[0].Text; //if its a directory, redirect to our page and passing //the new file path to our query string if(fileSystemType == "Directory") { Response.Redirect("theNewWayBrowser.aspx?d="+ e.CommandArgument.ToString()); } else { //get the filename string filename = e.CommandName; //read the file to our stream Stream s = File.OpenRead(filepath); //create the bytes to be streamed Byte[] buffer = new Byte[s.Length]; //build the buffer try { s.Read(buffer, 0, (Int32) s.Length); } //close our stream finally { s.Close(); } //clear the response headers Response.ClearHeaders(); //clear the content type Response.ClearContent(); Response.ContentType = "application/octet-stream"; //add our header Response.AddHeader("Content-Disposition", "attachment; filename=" + filename); //write the buffer to the http stream Response.BinaryWrite(buffer); //end response 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. If you are interested in getting the source code of this solution, you can download them in here
The C# version of this example: FolderBrowserCS.zip (36.43 KB) The VB.NET version of our file browser: FolderBrowserVB.zip (25.79 KB)
Note: I think i was not able to properly comment the VB.NET version. But i'll upload the properly commented version in the next few days.
Powered by: newtelligence dasBlog 2.3.9074.18820
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.
E-mail
Theme design by Jelle Druyts