Managing Remote Files with ColdFusion

At the request of a fellow EasyCFM Forum user, we?re going to discuss how to use ColdFusion to manage remote files on the server. While the original request was only for .txt files specifically, we?re going to look at handling .txt, .cfm, .cfml, .htm, and .html.

This can actually come in handy if you need to do a quick edit on a page on your Web site, but have no access to FTP clients...only a Web browser.

In order for the code shown in this tutorial to work, all files to be edited must reside on the Web server, in the web root directory, in a directory called ?myfiles?. The tutorial will assume the following path:

c:\inetpub\wwwroot\myfiles\

This application will consist of 5 templates:

remote_file_list.cfm ? displays a list of all available files
remote_file_add.cfm ? allows the user to create a new file
remote_file_edit.cfm ? allows the user to edit an existing file
remote_file_save.cfm ? saves the file to the server, either after an add or an edit
remote_file_delete.cfm ? deletes a file

NOTE: usually I like to include a link to a working sample of code with my tutorials. However, due to the nature of this tutorial (the ability to write files to the server), I?ve decided that an ounce of prevention is worth a pound of cure...and chickened out. I do, however, include all 5 files in a zip located at http://charlie.griefer.com/mrf.zip.

On to the tutorial...

remote_file_list.cfm:
********************************************
<cfscript>
    if (NOT structKeyExists(form, 'fileExt')) form.fileExt = "*";
</cfscript>

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>

<html xmlns=
"http://www.w3.org/1999/xhtml">
<head>
<title>
File List</title>
<meta http-equiv=
"Content-Type" content="text/html;charset=utf-8" />

<style type="text/css">
    body, td { font-family:verdana; font-size:11px; }
    a { color:#0000ff; text-decoration:none; }
    a:hover { color:#ff0000; text-decoration:underline; }
</style>
</head>

<body>

<!--- get the list of text files. this assumes they will all be in a directory called 'textfiles' under the web root --->
<cfdirectory action="list"
   
                  directory="c:\Inetpub\wwwroot\myfiles"
                  name=
"fileList"
                  filter=
"#form.fileExt#">

<table style="width:500px;" cellpadding="1" cellspacing="1">
    <form action="remote_file_list.cfm" method="post">
    <tr>
        <td style=
"vertical-align:bottom; padding-bottom:2px;">Select a File to Edit:</td>
        <td style=
"vertical-align:bottom; text-align:right;">File Type:
        <select name="fileExt" style="font-family:verdana; font-size:11px;" onchange="this.form.submit();">
            <option value="*"<cfif form.fileExt IS "*"> selected</cfif>>all files</option>
            <option value=
"*.cfm"<cfif form.fileExt IS "*.cfm"> selected</cfif>>cfm</option>
            <option value=
"*.htm"<cfif form.fileExt IS "*.htm"> selected</cfif>>html</option>
            <option value=
"*.txt"<cfif form.fileExt IS "*.txt"> selected</cfif>>txt</option>
        </select>

        </td>
    </tr>

    </form>
</table>

<table style="width:500px; border:1px #000000 solid;" cellpadding="1" cellspacing="1">
    <tr style=
"background-color:#cccccc;">
        <td style=
"font-weight:bold;">File Name&nbsp;&nbsp;</td>
        <td style=
"font-weight:bold; width:80px; text-align:right;">File Size&nbsp;&nbsp;</td>
        <td style=
"font-weight:bold; width:150px;">Last Modified&nbsp;&nbsp;</td>
        <td style=
"font-weight:bold; width:60px;">&nbsp;&nbsp;</td>
    </tr>

    <cfoutput query="fileList">
    <cfif left(fileList.name, 1) IS NOT ".">
    <!--- this condition does NOT need to be included in CF MX!! --->
    <tr style="background-color:<cfif currentRow MOD 2>##ffffff<cfelse>##ececec</cfif>;">
        <td><a href=
"remote_file_edit.cfm?file=#URLEncodedFormat(name)#">#name#</a>&nbsp;&nbsp;</td>
        <td style=
"text-align:right;">#int(evaluate(size/1024))# KB&nbsp;&nbsp;</td>
        <td>
#dateFormat(dateLastModified, 'mm/dd/yyyy')# #timeFormat(dateLastModified, 'h:mm tt')#&nbsp;&nbsp;</td>
        <td style=
"text-align:center;"><a href="remote_file_delete.cfm?file=#URLEncodedFormat(name)#" onclick="return confirm('Are You Sure You Wish to Delete This File?');">delete</a></td>
    </tr>

    </cfif>
    </cfoutput>

</table>
<br />
<a href="remote_file_add.cfm">Create a new file</a>

</body>
</html>

****************************************

Here we use a <cfdirectory> with a LIST action attribute. By default, there is no filter applied (all files are returned). In include a <select> form input allowing the user to determine the type of file he/she wishes to see (options being *.txt, *.html, and *.cfml). This is reflected in the FILTER attribute of the <cfdirectory> tag.
Looking at the docs for <cfdirectory>, you can see that it actually returns a named recordset (very similar to a cfquery), with specific ?columns?. I display a table which shows the values of those columns. Name, file size, and date last modified are displayed within the <cfoutput query=?fileList?></cfoutput> tags.

The Name column contains a link to remote_file_edit.cfm, and passed the file name in the URL (wrapped in a URLEncodedFormat() function to handle any non-URL-friendly characters). To make this more of a ?full? application, I also include a link with each record to remote_file_delete.cfm, also passing the name of the file in the URL. The links to remote_file_delete.cfm contain a JavaScript confirm() method, prompting the user as to whether or not he/she wishes to proceed with the delete. This is something I try to do standard on links that will delete any files or data.

Finally, a link exists underneath the existing files, allowing the user to create a new file.


remote_file_edit.cfm:
******************************************
<cfif NOT structKeyExists(URL, 'file')>
    <cflocation url=
"remote_file_list.cfm" />
    <cfabort />
</cfif>

<cfscript>

    variables.validFile = 1;

    if (NOT reFindNoCase('.txt$|.htm$|.html$|.cfm$|.cfml$', URL.file)) {
        variables.validFile = 0;
    }
</cfscript>

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>

<html xmlns=
"http://www.w3.org/1999/xhtml">
<head>
<title>
Editing <cfoutput>#URL.file#</cfoutput></title>
<meta http-equiv=
"Content-Type" content="text/html;charset=utf-8" />

<script language="JavaScript" type="text/javascript">
    function sTrim(sVariable) {
        return sVariable.replace(/^\s+|\s+$/g,"");
    }

    function validateFields(form) {

        if (sTrim(form.fileContent.value) == "") {
            alert('The file must have content!');
            form.fileContent.focus();
            return false;
        }
    return true;
    }
</script>

<style type="text/css">
   
body, td { font-family:verdana; font-size:11px; }
    a { color:#0000ff; text-decoration:none; }
    a:hover { color:#ff0000; text-decoration:underline; }
</style>
</head>

<body>

<cfif NOT variables.validFile>
    <cfoutput>

        <span style="font-weight:bold;">#URL.file#</span> is not an acceptable file type to edit.
        <br /><br />
        <a href="remote_file_list.cfm">click here to continue</a>
    </cfoutput>

<cfelse>

    <cffile action="read"
              file=
"c:\inetpub\wwwroot\myfiles\#URL.file#"
              variable=
"thisFile">

    <cfoutput>
    <form action="remote_file_save.cfm" method="post" onsubmit="return validateFields(this);">
    <input type=
"hidden" name="fileName" value="#URL.file#" />
    <input type=
"hidden" name="action_type" value="edit" />

    <table border="0" style="width:600px;">
        <tr>
            <td style=
"font-weight:bold; width:120px;">Currently Editing:&nbsp;</td>
            <td style=
"width:480px;">#URL.file#</td>
        </tr>
        <tr>
            <td colspan=
"2"><textarea name="fileContent" style="font-family:verdana; font-size:11px; height:250px; width:600px;">#thisFile#</textarea>
            </td>
        </tr>
        <tr>
            <td colspan=
"2" style="text-align:right;">
<input type="button" value="cancel" style="font-family:verdana; font-size:11px;" onclick="location.href='remote_file_list.cfm';" />
<input type=
"submit" value="edit file >" style="font-family:verdana; font-size:11px;" />
            </td>
        </tr>
    </table>

    </form>
    </cfoutput>

</cfif>

</body>
</html>

*********************************************

Before doing anything on the remote_file_edit.cfm page, we need to make sure a URL variable named ?file? was passed. If not, we send the user back to the main page. We are then going to use <cffile> with an ACTION=?read? attribute. However, before reading the file, we need to make sure it?s a ?valid? file (eg a text-based file). I use an reFindNoCase() function to ensure that the file extension of the specified file is either .txt, .cfm, .cfml, .htm, or .html. If this condition is not met, the user is informed that he or she picked a file type that can not be edited, and a link to the first page is displayed.

Assuming a valid file type, the <cffile action=?read?> is run on the specified file, and the content saved into a variable called ?thisFile?.

The rest of the code is a simple HTML form, with a couple of hidden form fields (one to tell the action page that the user was editing a file, not adding one...and one with the file name), and one textarea which will contain the contents of the file.

There is a JavaScript function to validate the form prior to submission, ensuring that there is content inside of the textarea (a zero-length file cannot be saved).



remote_file_save.cfm:
************************************************
<cfif NOT structKeyExists(form, 'fileName')>
    <cflocation url=
"remote_file_list.cfm" />
    <cfabort />
</cfif>


<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>

<html xmlns=
"http://www.w3.org/1999/xhtml">
<head>
<title>
File Saved</title>
<meta http-equiv=
"Content-Type" content="text/html;charset=utf-8" />

<style type="text/css">
    body, td { font-family:verdana; font-size:11px; }
    a { color:#0000ff; text-decoration:none; }
    a:hover { color:#ff0000; text-decoration:underline; }
</style>
</head>

<body>


<cffile action="write"
         file=
"c:\inetpub\wwwroot\myfiles\#form.fileName#"
         output=
"#form.fileContent#"
         addnewline=
"no">

<span style="font-weight:bold;"><cfoutput>#form.fileName#</cfoutput></span> 
has been <cfif form.action_type IS "edit">updated<cfelse>written</cfif> successfully.

<br /><br />

<a href="remote_file_list.cfm">click here to continue</a>

</body>
</html>

*********************************************

In remote_file_save.cfm, we first check for the existence of the form field ?fileName?. If it does not exist, we send the user back to the main page. Once we?re passed that, we run a <cffile> with an ACTION=?write? attribute to create the specified file. Since we?re specifying a filename that already exists, we will essentially be overwriting the original file. 

Because remote_file_save.cfm is the action page for both editing and adding a file, we need to run a quick condition to check the value of form.action_type before displaying the confirmation message to the user.

A link is provided to return the user to the main page.


remote_file_add.cfm:
*****************************************
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>

<html xmlns=
"http://www.w3.org/1999/xhtml">
<head>
<title>
Untitled</title>
<meta http-equiv=
"Content-Type" content="text/html;charset=utf-8" />

<cfdirectory action="list"
                 directory=
"c:\Inetpub\wwwroot\textfiles"
                 name=
"fileList"
                 filter=
"*.txt">

<script language="JavaScript" type="text/javascript">
    var fileArray = new Array(<cfoutput>#quotedValueList(fileList.name)#</cfoutput>);

    function sTrim(sVariable) {
        return sVariable.replace(/^\s+|\s+$/g,"");
    }

    function validateFields(form) {
        var fileCount = 0;
        var re = /.txt$|.cfm$|.cfml$|.htm|.html$/;

        // has the user entered a file name?
        if (sTrim(form.fileName.value) == "") {
            alert('You Must Enter a File Name');
            form.fileName.focus();
            return false;
        }

        // make sure the file name ends in .txt
        if (form.fileName.value.search(re) < 0) {
            alert('Unacceptable File Extension!\n\n.cfm, .cfml, .htm, .html, and .txt Only!');
            form.fileName.focus();
            form.fileName.select();
            return false;
        }

        for (var i=0; i<fileArray.length; i++) {
            if (sTrim(form.fileName.value) == fileArray[i]) {

                fileCount++;
            } 
        }


       

About This Tutorial
Author: Charlie Griefer (CJ)
Skill Level: Intermediate 
 
 
 
Platforms Tested: CF5
Total Views: 42,890
Submission Date: June 03, 2003
Last Update Date: June 05, 2009
All Tutorials By This Autor: 15
Discuss This Tutorial
  • right first of all great coding. i got one little problem though. i've got all of the codings to work. the only problem i'm getting is when i save a new file i have created, the file does not show on the list. i think my problem is the root directory not 100% sure. heres what i've written down where you have written directory="c:\Inetpub\wwwroot\myfiles" directory= "O:\TG-54-H\40 - WEB\Projects\Nadeem\testing\myfiles" please tell if it is right or wrong, or what else could i check

  • Will this work on a load balanced server environment?

  • Once I made the slight change to the edit page as noted above, this is EXACTLY what I needed. This code was a GREAT way to get to those files allowing me to update them as needed. Now I can use my evenings for somethig else! Thanks EasyCFM!

  • >#thisFile# ya need to change it to this >#htmleditformat(thisFile)# if you don't do this the coldfusion can get confused between the textarea code and the actual file code Try and edit the remote_file_add.cfm and the remote_file_edit.cfm files with these files without doing the change that i specified above and you will know what I mean.

  • Yes, it seems that the first closing tag "", present in the file that is edited, goes in "conflict" with the closing tags with something else, than when you submit the form do the same but this time placing back the tags.

  • it's seem got some problem when i try to edit a cfm file that have a text area input.

Advertisement

Sponsored By...
Powered By...