Could I get some help working with files using FileSystemObject?
This article has been here for a long time, but previously, it merely pointed to Microsoft's documentation.
I've had a few comments that I should write up my thoughts on FileSystemObject, and what kind of approaches I use when trying to solve problems that involve ASP and the file system. In addition, some comments have come by that state the Microsoft documentation is lacking. It is certainly thorough, but there are a few discrepancies I'd like to clear up along the way, as well.
So, here it is. I'm going to break down a bunch of areas where I've worked with FileSystemObject, show some quasi-useful code samples, and hopefully point out some potential pitfalls and how to avoid common errors and problems.
First off, make sure you have the most recent scripting engines, from Microsoft Script Downloads. This is because some of the methods and properties were added to FileSystemObject as new releases came out, and it can never hurt to have the most recent version anyway.
Also, make sure Norton Anti-Virus is *not* installed; if it is there, make sure the "Script Blocking" feature is disabled. Otherwise, any script that tries to use FileSystemObject will just spin in your browser (see Article #2180 for more details).
Now, before we start working on these examples, we're going to create an include file that will help us instantiate Scripting.FileSystemObject. We'll make a simple call to a sub, rather than repeatedly typing in Set fso = CreateObject("Scripting.FileSystemObject") for every sample.
We're also going to be using the Server.MapPath() function quite a lot, so we can stuff that into the header as well. When passing a filename to FileSystemObject, it expects a full local path. If you just supply a filename, it will attempt to create the file in the working directory for FileSystemObject, which by default is %SYSDIR%. This will either cause very unexpected results (especially if you manage to overwrite an important file), or the following error message:
So, you will see that in all of the examples, we use this Map() function in the #include file below to turn the filename into a local path. In this article, the files will always be placed in the same folder as the ASP script; however, don't feel like that has to be the case. Just make sure that whatever folder you decide to use is accessible (both read and write) by IUSR_MachineName. To ensure that the Internet Guest Account has read and write permissions to the directory, right-click the folder name in Windows Explorer, hit Properties, and make sure that the Security tab looks something like this:
If the Internet Guest Account is not listed, click Add... and find or type <computer_name>\IUSR_<computer_name>. Then, make sure that Read/Write are both enabled.
Whew. Now that that's all taken care of, save the following code in a file called fsoHeader.asp:
And then for all of the samples, assume that this line of code is in place:
In addition to saving your fingers, it will save this article from being cluttered with the same code over and over again. For similar reasons, I am going to avoid using Option Explicit and 80,000 Dim statements.
If you get the following error, it probably means you copied one of the samples, and forgot to add the above <!--#include--> directive:
Please be sure to add the #include line to the beginning of any of the samples. I'm not going to include it in all samples, in an effort to keep the code clear and concise.
Back to top
Creating a file
To create a text file, you use the CreateTextFile() method, which returns a TextStream object. This method takes a required parameter filename, and two optional parameters: overwrite and unicode. The optional parameter overwrite dictates how FileSystemObject should behave if the file already exists. I'm going to leave unicode parameters for a more advanced tutorial; typically, you will only be dealing with ASCII files when using the FileSystemObject, and luckily for the complexity of our examples, ASCII is the default.
Here is an example where we just pass the filename, and write one line of text to the file using the writeline() method.
Now, this example is supposed to fail if the file already exists (according to the CreateTextFile() method documentaion in the MSDN Library). However, it doesn't behave correctly, at least in version 5.6 of the scripting engine (scrrun.dll, the DLL used by FileSystemObject, has a default of TRUE for the overwrite parameter). Change the text in the WriteLine() call, and run the script again. You'll notice that the file was re-generated, and contains only the new line of text you entered. At the time of writing of this article (December 2003), the documentation hadn't been corrected to reflect the behavior, but Microsoft has been made aware of the issue.
In order to prevent being burned in the future by differences in documented and actual behavior of optional parameters, you might be better off being explicit than relying on the documentation.
Let's make sure that overwrite is enabled this time, and run the script again.
Now, let's change the optional overwrite parameter to false. You may want to do this in the situation that you want to create the file *only* if it exists; otherwise, return an error. If we use the same filename:
We should see this error:
There are at least three ways around this ugly error. We can use on error resume next:
We can use the FileExists() method first:
Or we can use the OpenTextFile() method, which will be defined in the next section.
Back to top
Appending to a file
Adding data to the end of a text file is very much like creating a text file and using WriteLine(). We use the OpenTextFile() method for this; like CreateTextFile(), there is a required parameter, filename. However, there are some new parameters, namely iomode (which tells the object to prepare the file for reading, writing, or appending), and create (which tells the object whether or not to create the file, in the case that it doesn't already exist). So that we don't have to memorize the reading, writing and appending constants for the iomode parameter, we can put them into our fsoHeader.asp file. (I'm not sure why VBScript lacks the feature of recognizing this type of constant the way VB can.) So add this to the bottom of the header file:
And again, there is a unicode parameter that we will be ignoring, since we will only be working with ASCII files. And, unlike overwrite, we can be fairly certain that Microsoft isn't going to change the default behavior for text file format (at least not before Longhorn <G>).
So, here is a quick sample that shows how to open a file, and write a line to it:
Note that if the file doesn't exist, you will receive the following error:
To make sure the file exists, you can use the FileExists() method as described above, or — if it's all right to completely overwrite the file if it does exist — add the create parameter.
If you want to create the new file when it doesn't already exist, or merely append to the existing file if it's already there, you can set iomode to ForAppending, and the create parameter to true. Here is an example:
When you view foo.txt, you will see "Line n?" and, if the file already existed prior to running the above code, you will see that this is the last line in the file, and follows any of the previous contents.
And here is a sample that creates a new text file, writes a line to it, and closes it; then opens the file and appends a second line to it:
You can use the write() method instead of the writeline() method, if you want to use multiple statements to append to only one line. This example should demonstrate (as well as show how the existing foo.txt is overwritten by the iomode of ForWriting):
And if you want to use a single statement to write multiple lines, you can use fs.writeline and the constant for carriage return / line feed pair, VBCrLf.
(You might find that using write() actually writes to the next line... if you use writeline() in the above sample, you can reproduce: there will be a trailing VBCrLf, and that causes the writing to start on the next line down.)
Back to top
Reading a file
To read a file's contents, you can simply use the OpenTextFile() method, with an iomode of ForReading. You can return the entire content of the file into a variable, or write it out to the page directly, using the ReadAll() method. For example:
Now of course, since the file is plain text, carriage returns are not going to be visible in HTML. One way to avoid this is by using <PRE></PRE>:
However, formatting leaves a lot to be desired. So, we need to replace the ASCII carriage returns/line feeds with HTML line feeds (e.g. "<br>"). I usually add a non-breaking space(" ") to each replacement, since some versions of Internet Explorer will ignore successive <br><br> tags, and multiple carriage returns might have been intentional.
Depending on the source of the file, you may have to experiment with replacing only CHR(13) or only CHR(10) rather than VBCrLf, because different editors and operating systems will use different combinations of CR/LF to enforce a carriage return. Another way would be to read each line individually, using the ReadLine() method and the AtEndOfStream property:
However, this is a cumbersome and inefficient way to process the file (since you have to maintain an open handle to the file throughout both the read and the write operations), and I usually recommend against using the AtEndOfStream proeprty at all — deal with the contents as a whole using ReadAll(). Usually, if I am not in control of how the files are created, I will use CHR(10) as my delimiter:
If you want an approximate line count in a file, you can use the split() method, like this:
I don't think there's a very efficient way to do this without actually retrieving the contents of the file. If the file doesn't exist, you will get the following error:
So, you might want to use the FileExists() methodology discussed previously in order to avoid potential errors if the filename is invalid. Note that if the file exists but contains *no* carriage returns (e.g. a one-liner), the lineCount will return 0. Similarly, if the last line of the file does not end with a carriage return, the count will be one short.
There may be cases where you want to retrieve a specific line in a file. For example, the second line, or the third last line, or the middle line. You can use a similar approach to getting the lineCount, except that we can locate a specific line's contents by referencing the nth element in the array — remembering that split() returns a 0-based array. Say we have a file, foobar.txt, that looks like this:
The following example will show how to get different lines:
A few notes about this solution. One is that the "middle" line will round down, if there are is an even number of lines. Another is that you should probably check what the ubound is before attempting to perform math on it; otherwise, you could end up with something like this:
What if you want to find all the lines containing a certain word? For example, with the file above (foobar.txt), I want to return all the lines that contain the word "splunge" (ignoring the fact that splunge isn't really a word). Well, you can do something like this:
If you want to read the properties of a file, such as the size, date modified, or dimensions (e.g. of a JPG file), see Article #2296.
Back to top
Continuing with the above example, let's say I wanted to modify the file, removing the lines that contain the word "splunge." We can use split() to build a new set of content after a read, then open the file for writing and dump the new contents to it.
What if you want to merge the contents of two files into one? Imagine a file foo_a.txt that contains the following:
And foo_z.txt contains the following:
Desired results would be foo_az.txt, with the following contents:
The following code will accomplish that:
And the following code will split the contents of one file into two files, by dividing the lines in half. You could use other criteria for deciding which file's contents to append to within the loop, of course.
Back to top
Renaming a file
FileSystemObject does not have a rename method, for some reason. There are two workarounds. One is to simply "move" the file using the MoveFile method, giving the destination a new name:
Another method is to actually obtain a file handle using GetFile(), and modify its read/write property "name":
In either case, if a file already exists with the new filename, you will get the following error:
So, similar to earlier examples, to get around this you could prevent this error by testing for the file's existence using the FileExists() method.
Back to top
Moving a file
Another obvious use for the MoveFile() method is to, well, move a file. So here is a way we could move a file into a subfolder of the current folder.
If the destination file already exists, you will get this error:
Again, like the other examples, you can avoid this error by using the FileExists() method or on error resume next.
Back to top
Copying a file
Another common task is to copy a file (or a set of files), for example to create a backup. Let's say that foo_a.txt is a very critical file and we like to back it up whenever we are playing with it, for example using it for FileSystemObject tutorials. And let's say we'd like to make a copy of it, in its current state, and place it in the backup/ subfolder. Well, we could do this:
Note that the CopyFile() method does not care if the file exists in the destination folder; it will overwrite it by default. To avoid overwriting an existing file, you could use the same FileExists() method we've talked about numerous times, or you could set the third parameter (overwrite) to false:
If you do this, and the file already exists, you will get the following error:
(Which you could trap with on error resume next.)
Back to top
Copying a set of files
This example will demonstrate a few additional techniques of FileSystemObject and the CopyFile() method. Let's say you have a folder with a bunch of TXT files and a bunch of GIF files, and you only want to make a backup of the GIF files. You can use iterate through the files collection, and test each extension using the GetExtensionName() method:
A much easier way would be to use wildcard matching, which is acceptable for the *source* of a CopyFile() call.
Note that wildcard characters can also be used when using the MoveFile() method, however they are only allowed in the *file* element of the path. In other words, you can't delete all test.txt files in all folders matching some pattern.
Back to top
Deleting a file
FileSystemObject has another convenient method: DeleteFile(). You can use this method to delete individual files (or, as with MoveFile(0 and CopyFile(), sets of files using a wildcard). Here is an example of how we would delete foo_a.txt:
Note that this will not work if foo_a.txt is currently set to read only. You will get the following error:
(You will also see this error if IUSR does not have write/modify permissions on the folder.)
Assuming permissions are adequate, and that the problem really is that the file has been erroneously marked as read-only, there are several ways to get around this. One is to use the force parameter of the DeleteFile() method:
Another is to test the Attributes property to see if the file is read-only, and if it is, switch that bit using XOR:
(Note that since we already have a valid handle to the File object, we could have opted to use its Delete() method rather than FSO's DeleteFile() method. The choice here is arbitrary; both methods work the same way.)
Here are the settings of the Attributes property:
Links to official documentation
Back to top
Related ArticlesCan I include a file in both server-side and client-side script?
Can I read / write a user's file without a prompt?
Can I rename a file using FileSystemObject?
How do I avoid 'the red x' when an image is missing?
How do I change the modified time of a file?
How do I create / manipulate images from ASP?
How do I dynamically include files?
How do I find the owner, author, and other properties of a file?
How do I get a list of a folder's subfolders?
How do I get the name of the current URL / page?
How do I prevent people from 'leeching' my CSS or JS files?
How do I prevent people from 'leeching' my images?
How do I retrieve a random file from a given folder?
How do I send the correct filename with BinaryWrite?
How do I sort a list of files?
How do I use FileSystemObject to create a file on the client?
Why do I get 'Disk not ready' errors with FileSystemObject?
Why do I get 'Invalid procedure call or argument'?
Why do I get 'Path not found' errors with Scripting.FileSystemObject?
Why do I get 'Permission Denied' errors with FileSystemObject?
Why do I get 800A0034 errors?
Why do I get 800A003E / Input past end of file errors?
Why do I get 800A0BBA errors?
Why do I get 800A0BBC errors?
Why do I get an 'Invalid Path Character' error?
Why do I get permissions errors after upgrading to Windows XP?
Why does FileSystemObject hang all of a sudden?
Why is 'the operation completed successfully' an error message?