Manipulating files is something that most computer programs do. ASP applications make use of the Scripting.FileSystemObject (FSO) to manipulate files and folders and read and write text files. The FSO provides a rich API to process files and folders.
The FSO includes an object model consisting of the following objects/collections
Object/Collection |
Description |
Main object. Contains methods and properties that allow you to create, delete, gain information about, and generally manipulate drives, folders, and files. Many of the methods associated with this object duplicate those in other FSO objects; they are provided for convenience. |
|
Object. Contains methods and properties that allow you to gather information about a drive attached to the system, such as its share name and how much room is available. Note that a "drive" isn't necessarily a hard disk, but can be a CD-ROM drive, a RAM disk, and so forth. A drive doesn't need to be physically attached to the system; it can be also be logically connected through a network. |
|
Collection. Provides a list of the drives attached to the system, either physically or logically. The Drives collection includes all drives, regardless of type. Removable-media drives need not have media inserted for them to appear in this collection. |
|
Object. Contains methods and properties that allow you to create, delete, or move a file. Also allows you to query the system for a file name, path, and various other properties. |
|
Collection. Provides a list of all files contained within a folder. |
|
Object. Contains methods and properties that allow you to create, delete, or move folders. Also allows you to query the system for folder names, paths, and various other properties. |
|
Collection. Provides a list of all the folders within a Folder. |
|
Object. Allows you to read and write text files. |
As you can see, the FSO contains abstractions for drives, folders, and files. You can access either a collection of each type of object or individual objects in a given collection based on your application’s needs. Note also that the FSO provides access to a TextStreamObject. You can use the TextStream object to read and write text files in your applications.
The FSO allows you to interact with the File system of the server programmatically. With the FSO, you can get information about drives, files, and folders, create and delete files and folders, and copy and move files and folders. Note that Drives are essentially read-only abstractions – for obvious reasons. You really wouldn’t want the ability to delete a drive from your server, would you?
The Drive object allows you to gain information about the various drives attached to a system, either physically or over a network. Its properties allow you to obtain information about:
The total size of the drive in bytes (TotalSize property)
How much space is available on the drive in bytes (AvailableSpace or FreeSpace properties)
What letter is assigned to the drive (DriveLetter property)
What type of drive it is, such as removable, fixed, network, CD-ROM, or RAM disk (DriveType property)
The drive's serial number (SerialNumber property)
The type of file system the drive uses, such as FAT, FAT32, NTFS, and so forth (FileSystem property)
Whether a drive is available for use (IsReady property)
The name of the share and/or volume (ShareName and VolumeName properties)
The path or root folder of the drive (Path and RootFolder properties)
Here’s a function that gets information from a drive:
function ShowDriveInfo1(drvPath)
{
var fso, drv, s ="";
fso = Server.CreateObject("Scripting.FileSystemObject");
drv = fso.GetDrive(fso.GetDriveName(drvPath));
s += "Drive " + drvPath.toUpperCase()+ " - ";
s += drv.VolumeName + "<br>";
s += "Total Space: " + drv.TotalSize / 1024;
s += " Kb" + "<br>";
s += "Free Space: " + drv.FreeSpace / 1024;
s += " Kb" + "<br>";
Response.Write(s);
}
As you can see, accessing a drive is actually pretty simple. First, instantiate the FSO. Then call the GetDrive() method on the FSO passing in a string representation of a drive (i.e. “c:\”). The function above then makes use of some of the properties of the drive object to build a display string. The display string outputs the drive letter, volume name, used space, and free space.
Following are some common tasks involving working with folders and the associated methods of the Folder and FSO objects:
Task |
Method |
Create a folder. |
FileSystemObject.CreateFolder |
Delete a folder. |
Folder.Delete or FileSystemObject.DeleteFolder |
Move a folder. |
Folder.Move or FileSystemObject.MoveFolder |
Copy a folder. |
Folder.Copy or FileSystemObject.CopyFolder |
Retrieve the name of a folder. |
Folder.Name |
Find out if a folder exists on a drive. |
FileSystemObject.FolderExists |
Get an instance of an existing Folder object. |
FileSystemObject.GetFolder |
Find out the name of a folder's parent folder. |
FileSystemObject.GetParentFolderName |
Find out the path of system folders. |
FileSystemObject.GetSpecialFolder |
You may have noticed that some of the methods are called from the Folder object and some from the FSO. It makes sense when you consider the activities of these methods. You wouldn’t want a Folder object to be able to delete itself for example. Rather, the Delete() method of the Folder object deletes child Folders of the current folder. This is merely a convenience as you could have built a path to the Folder object you’d like to delete and then called DeleteFolder() from the FSO. The same is true for moving and copying Folder objects.
Following is an example of getting information from, creating, and removing Folder objects:
function ShowFolderInfo()
{
var fso, fldr, s = "";
// Get instance of FileSystemObject.
fso = Server.CreateObject("Scripting.FileSystemObject");
// Get Drive object.
fldr = fso.GetFolder("c:"); //assumes the server has a ‘c:’ drive
// Print parent folder name.
Response.Write("Parent folder name is: " + fldr + "<br>");
// Print drive name.
Response.Write("Contained on drive " + fldr.Drive + "<br>");
// Print root file name.
if (fldr.IsRootFolder)
Response.Write("This is the root folder.");
else
Response.Write("This folder isn't a root folder.");
Response.Write("<br><br>");
// Create a new folder with the FileSystemObject object.
fso.CreateFolder ("C:\\Bogus");
Response.Write("Created folder C:\\Bogus" + "<br>");
// Delete the newly created folder.
fso.DeleteFolder ("C:\\Bogus");
Response.Write("Deleted folder C:\\Bogus" + "<br>");
}
Displaying the objects contained within a Folder object is also rather easy. First, get a handle to a Folder object. Then, wrap an Enumerator around one of the child collections and loop through it processing each item in the enumerator until you run out of items. An Enumerator is essentially a list of the same type of objects that includes methods that make it easy to move through the list. Hre’s an example function that lists sub-folders of a given folder:
function ShowSubFolders(fldr)
{
var fso, f, fEnum, s;
fso = Server.CreateObject("Scripting.FileSystemObject");
f = fso.GetFolder(Server.MapPath(fldr));
fEnum = new Enumerator(f.SubFolders);
fEnum.moveFirst();
s = "";
while (!fEnum.atEnd())
{
s += fEnum.item();
s += "<br>";
fEnum.moveNext();
}
return(s);
}
You can use the above code to list all of the sub-folders of a given folder by passing a reference to that folder as an argument to the function. Here’s an example that lists all of the files in a folder using the same mechanism:
function showFiles(folderspec){
var fso, f, fc, s, file;
fso = Server.CreateObject("Scripting.FileSystemObject");
f = fso.GetFolder(Server.MapPath folderspec));
fc = new Enumerator(f.files);
fc.moveFirst();
s = "";
while(!fc.atEnd()){
file = fc.item();
s += '<a href="’ + file.Name + '">' + file.Name + '</a><br>\n';
fc.moveNext()
}
return(s);
}
The showFiles() method is quite useful – especially in a web environment. I’ve used the showFiles() method to enable document repositories. In other words, I can simply dump documents into a directory, regardless of their type, and list them with the showFiles() method. This is especially useful for things like forms or committee minutes. I just place the documents in the folder and they are instantly accessible.
The FSO has 2 methods each for moving, copying, and deleting files:
Task |
Method |
Move a file |
File.Move or FileSystemObject.MoveFile |
Copy a file |
File.Copy or FileSystemObject.CopyFile |
Delete a file |
File.Delete or FileSystemObject.DeleteFile |
Following is an example that makes use of the above methods:
function ManipFiles()
{
var fso, f1, f2, s;
fso = new ActiveXObject("Scripting.FileSystemObject");
f1 = fso.CreateTextFile("c:\\testfile.txt", true);
Response.Write("Writing file <br>");
// Write a line.
f1.Write("This is a test.");
// Close the file to writing.
f1.Close();
Response.Write("Moving file to c:\\tmp <br>");
// Get a handle to the file in root of C:\.
f2 = fso.GetFile("c:\\testfile.txt");
// Move the file to \tmp directory.
f2.Move ("c:\\tmp\\testfile.txt");
Response.Write("Copying file to c:\\temp <br>");
// Copy the file to \temp.
f2.Copy ("c:\\temp\\testfile.txt");
Response.Write("Deleting files <br>");
// Get handles to files' current location.
f2 = fso.GetFile("c:\\tmp\\testfile.txt");
f3 = fso.GetFile("c:\\temp\\testfile.txt");
// Delete the files.
f2.Delete();
f3.Delete();
Response.Write("All done!");
}
The above creates a text file in the root directory of drive C, writes some information to it, moves it to a directory called \tmp, makes a copy of it in a directory called \temp, then deletes the copies from both directories.
To run the following example, create directories named \tmp and \temp in the root directory of drive C:
In addition to manipulation, you can get information form files through the File object’s properties. Following is an example that displays information about a File:
function ShowFileAccessInfo(filespec)
{
var fso, f, s;
fso = Server.CreateObject("Scripting.FileSystemObject");
f = fso.GetFile(filespec);
s = f.Path.toUpperCase() + "<br>";
s += "Created: " + f.DateCreated + "<br>";
s += "Last Accessed: " + f.DateLastAccessed + "<br>";
s += "Last Modified: " + f.DateLastModified
return(s);
}
You can use the above function to alert users when files have changed. It’s an easy way to let your users know when something new happens.
There are three ways to create an empty text file (sometimes referred to as a "text stream").
The first way is to use the CreateTextFile method. The following example demonstrates how to create a text file using the CreateTextFileMethod method.
var fso, f1;
fso = Server.CreateObject("Scripting.FileSystemObject");
f1 = fso.CreateTextFile("c:\\testfile.txt", true);
The second way to create a text file is to use the OpenTextFile method of the FileSystemObject object with the ForWriting flag set.
var fso, ts;
var ForWriting= 2;
fso = Server.CreateObject("Scripting.FileSystemObject");
ts = fso.OpenTextFile("c:\\test.txt", ForWriting, true);
A third way to create a text file is to use the OpenAsTextStream method with the ForWriting flag set.
var fso, f1, ts;
var ForWriting = 2;
fso = Server.CreateObject("Scripting.FileSystemObject");
fso.CreateTextFile ("c:\\test1.txt");
f1 = fso.GetFile("c:\\test1.txt");
ts = f1.OpenAsTextStream(ForWriting, true);
Once the text file is created, add data to the file using the following three steps:
Open the text file.
Write the data.
Close the file.
To open an existing file, use either the OpenTextFile method of the FileSystemObject object or the OpenAsTextStream method of the File object.
To write data to the open text file, use the Write, WriteLine, or WriteBlankLines methods of the TextStream object, according to the tasks outlined in the following table.
Task |
Method |
Write data to an open text file without a trailing newline character. |
Write |
Write data to an open text file with a trailing newline character. |
WriteLine |
Write one or more blank lines to an open text file. |
WriteBlankLines |
To close an open file, use the Close method of the TextStream object.
Note The newline character contains a character or characters (depending on the operating system) to advance the cursor to the beginning of the next line (carriage return/line feed). Be aware that the end of some strings may already have such nonprinting characters.
The following example demonstrates how to open a file, use all three write methods to add data to the file, and then close the file:
function CreateFile()
{
var fso, tf;
fso = Server.CreateObject("Scripting.FileSystemObject");
tf = fso.CreateTextFile("c:\\testfile.txt", true);
// Write a line with a newline character.
tf.WriteLine("Testing 1, 2, 3.") ;
// Write three newline characters to the file.
tf.WriteBlankLines(3) ;
// Write a line.
tf.Write ("This is a test.");
tf.Close();
}
To read data from a text file, use the Read, ReadLine, or ReadAll method of the TextStream object. The following table describes which method to use for various tasks.
Task |
Method |
Read a specified number of characters from a file. |
Read |
Read an entire line (up to, but not including, the newline character). |
ReadLine |
Read the entire contents of a text file. |
ReadAll |
If you use the Read or ReadLine method and want to skip to a particular portion of data, use the Skip or SkipLine method. The resulting text of the read methods is stored in a string which can be displayed in a control, parsed by string functions (such as Left, Right, and Mid), concatenated, and so forth.
The following example demonstrates how to open a file, write to it, and then read from it:
function ReadFiles()
{
var fso, f1, ts, s;
var ForReading = 1;
fso = new ActiveXObject("Scripting.FileSystemObject");
f1 = fso.CreateTextFile("c:\\testfile.txt", true);
// Write a line.
Response.Write("Writing file <br>");
f1.WriteLine("Hello World");
f1.WriteBlankLines(1);
f1.Close();
// Read the contents of the file.
Response.Write("Reading file <br>");
ts = fso.OpenTextFile("c:\\testfile.txt", ForReading);
s = ts.ReadLine();
Response.Write("File contents = '" + s + "'");
ts.Close();
}
Although the FSO provides a robust API for ASP applications, be aware that it doesn’t perform magic! You must still consider, for example, the issue if sharing files between multiple users – especially those files accessed through a web application.
Here’s a scenario illustrating how difficult it can be to manage files within a web application:
Suppose you have 2 users who wish to write to the same file. User 1 opens the file for writing. Then user 2 opens the file for writing. User 1’s phone rings, she answers, talks for a while, then hangs up. She’s now ready to work with the file. User 2, however, has already completed his writing to the file while user 1 was on the phone. So, an interesting situation arises: user 1 is looking at stale data. Even worse, when user1 saves her changes, they’ll overwrite user 2’s data!
So, how do you eliminate these kinds of problems? Well, the answer to that one isn’t so easy. You could lock the application while user1 has the file open. That’s not very economical nor friendly as user 2 will be blocked from completing his task while the application is locked.
A better idea would be to check the attributes of the file to be written before it is written. That way you could check whether or not to simply write the file or merge the two edits together. Of course, you’d then need to write your own merge routine (sounds difficult but really isn’t – just append to the end rather than overwrite). Still, that could add up to more work than is necessary.
In short, there is no easy answer to the problem of shared resources. Just make sure that you take into account the issues when designing your applications.