Tuesday, November 5, 2024

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:inetpubwwwrootmyfiles

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/code/tutorials/rfm.zip.

On to the tutorial…

remote_file_list.cfm:



<cfscript>
&nbsp 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">
&nbsp &nbsp body, td { font-family:verdana; font-size:11px; }
&nbsp &nbsp a { color:#0000ff; text-decoration:none; }
&nbsp 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
&nbsp &nbsp &nbsp called 'textfiles' under the web root --->
<cfdirectory action="list"
&nbsp &nbsp &nbsp directory="c:Inetpubwwwrootmyfiles"
&nbsp &nbsp &nbsp name="fileList"
&nbsp &nbsp &nbsp filter="#form.fileExt#">

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

<table style="width:500px; border:1px #000000 solid;" cellpadding="1" cellspacing="1">
&nbsp &nbsp <tr style="background-color:#cccccc;">
&nbsp &nbsp &nbsp <td style="font-weight:bold;">File Name  </td>
&nbsp &nbsp &nbsp <td style="font-weight:bold; width:80px; text-align:right;">File Size  </td>
&nbsp &nbsp &nbsp <td style="font-weight:bold; width:150px;">Last Modified  </td>
&nbsp &nbsp &nbsp <td style="font-weight:bold; width:60px;">  </td>
&nbsp &nbsp &nbsp </tr>
&nbsp &nbsp <cfoutput query="fileList">
&nbsp &nbsp <cfif left(fileList.name, 1) IS NOT ".">
&nbsp &nbsp <!--- this condition does NOT need to be included in CF MX!! --->
&nbsp &nbsp <tr style="background-color:<cfif currentRow MOD 2>##ffffff<cfelse>##ececec</cfif>;">
<td><a href="remote_file_edit.cfm?file=#URLEncodedFormat(name)#">#name#</a>  </td>
&nbsp &nbsp <td style="text-align:right;">#int(evaluate(size/1024))# KB  </td>
&nbsp &nbsp <td>#dateFormat(dateLastModified, 'mm/dd/yyyy')# #timeFormat(dateLastModified, 'h:mm tt')#  </td>
&nbsp &nbsp <td style="text-align:center;"><a href="remote_file_delete.cfm?file=#URLEncodedFormat(name)#"
&nbsp &nbsp &nbsp onclick="return confirm('Are You Sure You Wish to Delete This File?');">delete</a></td>
&nbsp &nbsp </tr>
&nbsp &nbsp </cfif>
&nbsp &nbsp </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')>
&nbsp <cflocation url="remote_file_list.cfm" />
&nbsp <cfabort />
</cfif>

<cfscript>
&nbsp variables.validFile = 1;

&nbsp if (NOT reFindNoCase('.txt$|.htm$|.html$|.cfm$|.cfml$', URL.file)) {
&nbsp &nbsp variables.validFile = 0;
&nbsp }
</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">
&nbsp function sTrim(sVariable) {
&nbsp &nbsp return sVariable.replace(/^s+|s+$/g,"");
&nbsp }

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

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

<body>

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

<cfelse>

&nbsp <cffile action="read"
&nbsp &nbsp &nbsp file="c:inetpubwwwrootmyfiles#URL.file#"
&nbsp &nbsp &nbsp variable="thisFile">

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

&nbsp <table border="0" style="width:600px;">
&nbsp &nbsp <tr>
&nbsp &nbsp &nbsp <td style="font-weight:bold; width:120px;">Currently Editing: </td>
&nbsp &nbsp &nbsp <td style="width:480px;">#URL.file#</td>
&nbsp &nbsp </tr>
&nbsp &nbsp <tr>
&nbsp &nbsp &nbsp <td colspan="2"><textarea name="fileContent"
&nbsp &nbsp &nbsp style="font-family:verdana; font-size:11px; height:250px; width:600px;">#thisFile#</textarea>
&nbsp &nbsp &nbsp </td>
&nbsp &nbsp </tr>
&nbsp &nbsp <tr>
&nbsp &nbsp &nbsp <td colspan="2" style="text-align:right;">
<input type="button" value="cancel" style="font-family:verdana; font-size:11px;"
&nbsp &nbsp &nbsp onclick="location.href='remote_file_list.cfm';" />
<input type="submit" value="edit file >" style="font-family:verdana; font-size:11px;" />
&nbsp &nbsp &nbsp </td>
&nbsp &nbsp &nbsp </tr>
&nbsp </table>
&nbsp </form>
&nbsp </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')>
&nbsp <cflocation url="remote_file_list.cfm" />
&nbsp <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">
&nbsp body, td { font-family:verdana; font-size:11px; }
&nbsp a { color:#0000ff; text-decoration:none; }
&nbsp a:hover { color:#ff0000; text-decoration:underline; }
</style>
</head>

<body>

<cffile action="write"
&nbsp &nbsp file="c:inetpubwwwrootmyfiles#form.fileName#"
&nbsp &nbsp output="#form.fileContent#"
&nbsp &nbsp 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"
&nbsp &nbsp &nbsp directory="c:Inetpubwwwroottextfiles"
&nbsp &nbsp &nbsp name="fileList"
&nbsp &nbsp &nbsp filter="*.txt">

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

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

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

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

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

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

&nbsp &nbsp fileCount++;
&nbsp &nbsp }
&nbsp &nbsp }

&nbsp &nbsp // is the file name going to conflict with an existing file name?
&nbsp &nbsp if (fileCount > 0) {
&nbsp &nbsp alert('The File Name You Selected Already Exists! Please Choose Another Name');
&nbsp &nbsp form.fileName.focus();
&nbsp &nbsp form.fileName.select();
&nbsp &nbsp return false;
&nbsp &nbsp }

&nbsp &nbsp // has the user entered content for the file?
&nbsp &nbsp if (sTrim(form.fileContent.value) == "") {
&nbsp &nbsp alert('The file must have content!');
&nbsp &nbsp form.fileContent.focus();
&nbsp &nbsp return false;
&nbsp &nbsp }
&nbsp return true;
&nbsp }
</script>

<style type="text/css">
&nbsp body, td { font-family:verdana; font-size:11px; }
</style>
</head>

<body>

<cfoutput>
<form action="remote_file_save.cfm" method="post" onsubmit="return validateFields(this);">
<input type="hidden" name="action_type" value="add" />
<table border="0" style="width:400px;">
&nbsp <tr>
&nbsp &nbsp <td style="font-weight:bold;" nowrap="nowrap">File Name:  </td>
&nbsp &nbsp <td><input type="text" name="fileName"
style="font-family:verdana; font-size:11px; width:316px;" /></td>
&nbsp </tr>
&nbsp <tr>
&nbsp &nbsp <td colspan="2">
&nbsp &nbsp <textarea name="fileContent" style="font-family:verdana;
&nbsp &nbsp &nbsp font-size:11px; height:250px; width:600px;"></textarea>
&nbsp &nbsp </td>
&nbsp </tr>
&nbsp <tr>
&nbsp &nbsp <td colspan="2" style="text-align:right;">
&nbsp &nbsp <input type="button" value="cancel" style="font-family:verdana; font-size:11px;"
&nbsp &nbsp &nbsp onclick="location.href='textfile_reader.cfm';" />
&nbsp &nbsp <input type="submit" value="add file >" style="font-family:verdana; font-size:11px;" />
&nbsp &nbsp </td>
&nbsp </tr>
</table>
</form>
</cfoutput>

</body>
</html>


remote_file_add.cfm is very similar to remote_file_edit.cfm. One major difference is the user is specifying the new file name, so a JavaScript function runs (on form submit) to ensure a valid file extension (again, .cfm, .cfml, .htm, .html, and .txt).

remote_file_delete.cfm:


<cfif NOT structKeyExists(URL, 'file')>
&nbsp <cflocation url="textfile_reader.cfm" />
&nbsp <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>Delete <cfoutput>#URL.file#</cfoutput></title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />

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

<body>

<cffile action="delete"
&nbsp &nbsp file="c:inetpubwwwrootmyfiles#URL.file#">

<span style="font-weight:bold;"><cfoutput>#URL.file#</cfoutput></span> has been deleted.
<br /><br />
<a href="remote_file_list.cfm">click here to continue</a>

</body>
</html>


remote_file_delete.cfm is one of the simpler templates. After checking for the existence of a URL variable named file’, it simply runs a <cffile> with an <ACTION=”delete”> attribute. The user is then presented with a message indicating a successful deletion, with a link back to the main page.

Questions? I spend way too much time in the Easycfm.com forums. Meet me there and I’ll see if I can answer em for ya 🙂

* Originally Published at EasyCFM.com

Click here to sign up for FREE tech newsletters from Murdok!

Charlie lives in Phoenix, AZ with his lovely wife and daughter, two dogs,
and a pig. He has been using ColdFusion since version 1.5, and likes to
think that one day he’ll be good at it. A frequent contributor to Easycfm, Charlie enjoys writing ColdFusion tutorials, and
helping others in the forums. He invites you to view his own site at
http://charlie.griefer.com or drop him an email at charlie@griefer.com.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles