Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1140 Chapter 25: File I/O and Streams Working with Drives, Directories, and Files Many times in your ASP.NET applications, you need to interact with the local file system, reading directory structures, reading and writing to files, or performing many other tasks. The System.IO namespace within the .NET Framework makes working with file system directories and files very easy. While working with the classes in the System.IO namespace, keep in mind that because your ASP.NET applications are executing on the server, the file system you are accessing is the one your Web application is running on. You, of course, cannot use an ASP.NET application to access the end user’s file system. The DriveInfo Class You can start working with the System.IO namespace at the top of the directory tree by using a great new addition to the .NET 3.5 class libraries, the DriveInfo class. This class supplements the GetLog- icalDrives() method of the Directory class included in prior versions of the .NET Framework. It provides you with extended information on any drive registered with the server’s local file system. You can get information such as the name, type, size, and status of each drive. Listing 25-1 shows you how to create a DriveInfo object and display local drive information on a Web page. Listing 25-1: Displaying local drive information VB < script runat="server" > Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Dim drive As New System.IO.DriveInfo("C: \ ") lblDriveName.Text = drive.Name lblDriveType.Text = drive.DriveType.ToString() lblAvailableFreeSpace.Text = drive.AvailableFreeSpace.ToString() lblDriveFormat.Text = drive.DriveFormat lblTotalFreeSpace.Text = drive.TotalFreeSpace.ToString() lblTotalSize.Text = drive.TotalSize.ToString() lblVolumeLabel.Text = drive.VolumeLabel End Sub < /script > < html xmlns="http://www.w3.org/1999/xhtml" > < head runat="server" > < title > Displaying Drive Information < /title > < /head > < body > < form id="form1" runat="server" > < div > < table > < tr >< td > Drive Name: < /td >< td > < asp:Label ID="lblDriveName" runat="server" Text="Label" / > < /td >< /tr > < tr >< td > Drive Type: < /td >< td > < asp:Label ID="lblDriveType" runat="server" Text="Label"/ > < /td >< /tr > < tr >< td > Available Free Space: < /td >< td > < asp:Label ID="lblAvailableFreeSpace" runat="server" Text="Label" / > < /td >< /tr > < tr >< td > Drive Format: < /td >< td > < asp:Label ID="lblDriveFormat" runat="server" Text="Label" / > 1140 Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1141 Chapter 25: File I/O and Streams < /td >< /tr > < tr >< td > Total Free Space: < /td >< td > < asp:Label ID="lblTotalFreeSpace" runat="server" Text="Label" / > < /td >< /tr > < tr >< td > Total Size: < /td >< td > < asp:Label ID="lblTotalSize" runat="server" Text="Label" / > < /td >< /tr > < tr >< td > Volume Label < /td >< td > < asp:Label ID="lblVolumeLabel" runat="server" Text="Label" / > < /td >< /tr > < /table > < /div > < /form > < /body > < /html > C# < script runat="server" > protected void Page_Load(object sender, EventArgs e) { System.IO.DriveInfo drive = new System.IO.DriveInfo(@"C: \ "); lblDriveName.Text = drive.Name; lblDriveType.Text = drive.DriveType.ToString(); lblAvailableFreeSpace.Text = drive.AvailableFreeSpace.ToString(); lblDriveFormat.Text = drive.DriveFormat; lblTotalFreeSpace.Text = drive.TotalFreeSpace.ToString(); lblTotalSize.Text = drive.TotalSize.ToString(); lblVolumeLabel.Text = drive.VolumeLabel; } < /script > One of the more interesting properties in the sample is the DriveType enumeration. This read-only enu- meration tells you what the drive type is, for example CD-ROM, Fixed, Ram, or Removable. Figure 25-1 shows you what the page looks like when you view it in a browser. Figure 25-1 1141 Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1142 Chapter 25: File I/O and Streams You can also enumerate through all the drives on the local file system by using the DriveInfo ’s static GetDrives() method. Listing 25-2 shows an example of enumerating through the local file system drives and adding each drive as a root node to a TreeView control. Listing 25-2: Enumerating through local file system drives VB < script runat="server" > Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) If (Not Page.IsPostBack) Then For Each drive As System.IO.DriveInfo In System.IO.DriveInfo.GetDrives() Dim node As TreeNode = New TreeNode() node.Value = drive.Name ’ Make sure the drive is ready before we access it If (drive.IsReady) Then node.Text = drive.Name & _ " - (free space: " & drive.AvailableFreeSpace & ")" Else node.Text = drive.Name & " - (not ready)" End If Me.TreeView1.Nodes.Add(node) Next End If End Sub < /script > < html xmlns="http://www.w3.org/1999/xhtml" > < head runat="server" > < title > Enumerate Local System Drives < /title > < /head > < body > < form id="form1" runat="server" > < div > < table > < tr > < td style="width: 100px" valign="top" > < asp:TreeView ID="TreeView1" runat="server" >< /asp:TreeView > < /td > < /tr > < /table > < /div > < /form > < /body > < /html > C# < script runat="server" > protected void Page_Load(object sender, EventArgs e) { 1142 Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1143 Chapter 25: File I/O and Streams if (!Page.IsPostBack) { foreach (System.IO.DriveInfo drive in System.IO.DriveInfo.GetDrives()) { TreeNode node = new TreeNode(); node.Value = drive.Name; //Make sure the drive is ready before we access it if (drive.IsReady) node.Text = drive.Name + " - (free space: " + drive.AvailableFreeSpace + ")"; else node.Text = drive.Name + " - (not ready)"; this.TreeView1.Nodes.Add(node); } } } < /script > Notice that, in this sample, the drive object’s IsReady property is a read-only property used to test whether the drive is accessible. If you are enumerating drives, it’s always a good idea to test for this before attempting to access any of the other drive properties because removable drives and network drives may not always be available when your code is executed. Figure 25-2 shows what the page looks like when viewed in the browser. Figure 25-2 The Directory and DirectoryInfo Classes Next, you can build on the previous examples and add the capability to browse through the system’s directory structure. The System.IO namespace contains two classes for working with file system 1143 Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1144 Chapter 25: File I/O and Streams directories, the Directory and DirectoryInfo classes. The Directory class exposes static methods you can use to create, move, and delete directories. The DirectoryInfo represents a specific directory and lets you perform many of the same actions as the Directory class on the specific directory. Additionally, it enumerates child directories and files. To continue the example, you can use the GetDirectories() method of the DirectoryInfo class to create a recursive method that loops through each system drive directory tree and adds the directories to a TreeView control to create a small directory browser. Listing 25-3 shows how to create a recursive LoadDirectories() method to walk through the local file system’s directory structure. Listing 25-3: Enumerating file system directories VB < script runat="server" > Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) If (Not Page.IsPostBack) Then For Each drive As System.IO.DriveInfo In System.IO.DriveInfo.GetDrives() Dim node As TreeNode = New TreeNode() node.Value = drive.Name If (drive.IsReady) Then node.Text = drive.Name & _ " - (free space: " & drive.AvailableFreeSpace & ")" LoadDirectories(node, drive.Name) Else node.Text = drive.Name & " - (not ready)" End If Me.TreeView1.Nodes.Add(node) Next End If Me.TreeView1.CollapseAll() End Sub Private Sub LoadDirectories(ByVal parent As TreeNode, ByVal path As String) Dim directory As System.IO.DirectoryInfo = _ New System.IO.DirectoryInfo(path) Try For Each d As System.IO.DirectoryInfo In directory.GetDirectories() Dim node As TreeNode = New TreeNode(d.Name, d.FullName) parent.ChildNodes.Add(node) Continued 1144 Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1145 Chapter 25: File I/O and Streams ’Recurse the current directory LoadDirectories(node, d.FullName) Next Catch ex As System.UnauthorizedAccessException parent.Text += " (Access Denied)" Catch ex As System.IO.IOException parent.Text += " (Unknown Error: " + ex.Message + ")" End Try End Sub < /script > C# < script runat="server" > protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { foreach (System.IO.DriveInfo drive in System.IO.DriveInfo.GetDrives()) { TreeNode node = new TreeNode(); node.Value = drive.Name; if (drive.IsReady) { node.Text = drive.Name + " - (free space: " + drive.AvailableFreeSpace + ")"; LoadDirectories(node, drive.Name); } else node.Text = drive.Name + " - (not ready)"; this.TreeView1.Nodes.Add(node); } } this.TreeView1.CollapseAll(); } private void LoadDirectories(TreeNode parent, string path) { System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo(path); try { foreach (System.IO.DirectoryInfo d in directory.GetDirectories()) { TreeNode node = new TreeNode(d.Name, d.FullName); parent.ChildNodes.Add(node); Continued 1145 Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1146 Chapter 25: File I/O and Streams //Recurs the current directory LoadDirectories(node, d.FullName); } } catch (System.UnauthorizedAccessException e) { parent.Text += " (Access Denied)"; } catch (System.IO.IOException e) { parent.Text += " (Unknown Error: " + e.Message + ")"; } } < /script > Figure 25-3 shows what the page should look like in the browser. You should now be able to browse the directory tree, much as you do in Windows Explorer, by opening and closing the TreeView nodes. Figure 25-3 Notice that the example continuously creates new instances of the DirectoryInfo class each time the method executes in order to continue to enumerate the directory tree. You could also extend this example by displaying some additional properties as part of the Node text, such as the CreationTime or Attributes . To perform only a specific action, you don’t have to create an instance of the DirectoryInfo class. You can simply use the static methods exposed by the Directory class. These methods allow you to create, read properties from, and delete a directory. Rather than creating an object instance that represents a specific path and exposes methods that act on that path, the static methods exposed by the Directory 1146 Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1147 Chapter 25: File I/O and Streams class generally require you to pass the path as a method parameter. Listing 25-4 shows how you can use the static methods exposed by the Directory class to create, read properties from, and delete a directory. Remember to be very careful when deleting a folder from your hard drive. It is possible to permanently delete important data from your system or change the permissions of a resource, which would result in your losing the ability to access the resource. Listing 25-4: Working with the static methods of the Directory class VB < %@ Import Namespace="System.IO" % > < script runat="server" > Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Directory.CreateDirectory("C: \ Wrox") If Directory.Exists("C: \ Wrox") Then Me.Label1.Text = _ Directory.GetCreationTime("C: \ Wrox").ToString() Me.Label2.Text = _ Directory.GetLastAccessTime("C: \ Wrox").ToString() Me.Label3.Text = _ Directory.GetLastWriteTime("C: \ Wrox").ToString() Directory.Delete("C: \ Wrox") End If End Sub < /script > < html xmlns="http://www.w3.org/1999/xhtml" > < head runat="server" > < title > Using Static Methods < /title > < /head > < body > < form id="form1" runat="server" > < div > Creation Time: < asp:Label ID="Label1" runat="server" Text="Label" >< /asp:Label >< br / > Last Access Time: < asp:Label ID="Label2" runat="server" Text="Label" >< /asp:Label >< br / > Last Write Time: < asp:Label ID="Label3" runat="server" Text="Label" >< /asp:Label > < /div > < /form > < /body > < /html > Continued 1147 Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1148 Chapter 25: File I/O and Streams C# < %@ Import Namespace="System.IO" % > < script runat="server" > protected void Page_Load(object sender, EventArgs e) { Directory.CreateDirectory(@"C: \ Wrox"); if (Directory.Exists(@"C: \ Wrox") ) { this.Label1.Text = Directory.GetCreationTime(@"C: \ Wrox").ToString(); this.Label2.Text = Directory.GetLastAccessTime(@"C: \ Wrox").ToString(); this.Label3.Text = Directory.GetLastWriteTime(@"C: \ Wrox").ToString(); Directory.Delete(@"C: \ Wrox"); } } < /script > When you load this page in the browser, you will see that the Creation Time, Last Access Time ,and Last Write Time are displayed. Additionally, if you open Windows Explorer, you will see that the Wrox directory has been deleted. Using Relative Paths and Setting and Getting the Current Directory When an ASP.NET page is e xecuted, the thread used to execute the code that generates the page, by default,hasacurrentworkingdirectory.Itusesthisdirectory as its base directory if you have specified relative paths in your application. Therefore, if you pass a relative filename into any System.IO class, the file is assumed to be located in the current working directory. For example, the default working directory for the ASP.NET Development Server is a directory under your Visual Studio install root. If you installed Visual Studio in C: \ Program Files ,yourASP.NET Development Server working directory would be c: \ Program Files \ Microsoft Visual Studio 9.0 \ Common7 \ IDE . You can find the location of your working directory by using the Directory class’s GetCurrentDirec- tory() method. In addition, you can change the current working directory using the Directory class’s SetCurrentDirectory() method. Listing 25-5 shows you how to set and then display your working directory. Listing 25-5: Setting and displaying the application’s working directory VB < %@ Import Namespace="System.IO" % > < script runat="server" > 1148 Evjen c25.tex V2 - 01/28/2008 3:42pm Page 1149 Chapter 25: File I/O and Streams Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Me.Label1.Text = Directory.GetCurrentDirectory() Directory.SetCurrentDirectory("C: \ Wrox") Me.Label2.Text = Directory.GetCurrentDirectory() End Sub < /script > < html xmlns="http://www.w3.org/1999/xhtml" > < head runat="server" > < title > Set and Display the Working Directory < /title > < /head > < body > < form id="form1" runat="server" > < div > Old Working Directory: < asp:Label ID="Label1" runat="server" Text="Label" >< /asp:Label >< br / > New Working Directory: < asp:Label ID="Label2" runat="server" Text="Label" >< /asp:Label > < /div > < /form > < /body > < /html > C# < script runat="server" > protected void Page_Load(object sender, EventArgs e) { this.Label1.Text = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory(@"C: \ Wrox"); this.Label2.Text = Directory.GetCurrentDirectory(); } < /script > Note that the directory parameter you specify in the SetCurrentDirectory() method must already exist; otherwise, ASP.NET throws an exception. Knowing this, it would probably be a good idea to use the Exists() method of the Directory class to make sure the directory you are specifying does, in fact, already exist before you try to change the working directory. When you execute this code, you should see that it displays the original working directory, and then displays the new working directory after you change it. Figure 25-4 shows what the page looks like when executed. File and FileInfo Now that you can effectively display and browse a directory tree, you can expand the example even fur- ther by displaying the files located in the directory that is currently selected in your TreeView control. The simplest way to display the files is to bind a FileInfo array to a GridView . This example uses the GetFiles() method of the DirectoryInfo class because it returns an array of FileInfo objects. You want to use this method because the FileInfo object enables you to display some properties of each 1149 . current working directory using the Directory class’s SetCurrentDirectory() method. Listing 25- 5 shows you how to set and then display your working directory. Listing 25- 5: Setting and displaying the. directory for the ASP. NET Development Server is a directory under your Visual Studio install root. If you installed Visual Studio in C: Program Files ,yourASP .NET Development Server working directory. Evjen c 25. tex V2 - 01/28/2008 3: 42pm Page 1140 Chapter 25: File I/O and Streams Working with Drives, Directories, and Files Many times in your ASP. NET applications, you need to interact with