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 </td>
<td style="font-weight:bold; width:80px;
text-align:right;">File Size </td>
<td style="font-weight:bold;
width:150px;">Last Modified </td>
<td style="font-weight:bold;
width:60px;"> </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> </td>
<td style="text-align:right;">#int(evaluate(size/1024))# KB </td>
<td>#dateFormat(dateLastModified, 'mm/dd/yyyy')# #timeFormat(dateLastModified, 'h:mm tt')# </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: </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++;
}
}
part one of a three-part tutorial designed to gently introduce you to the world of complex variables.
Author: Charlie Griefer (CJ)
Views: 21,893
Posted Date: Monday, August 11, 2003
part two of a three-part tutorial designed to gently introduce you to the world of complex variables.
Author: Charlie Griefer (CJ)
Views: 14,862
Posted Date: Monday, August 11, 2003
part three of a three-part tutorial designed to gently introduce you to the world of complex variables.
Author: Charlie Griefer (CJ)
Views: 16,838
Posted Date: Monday, August 11, 2003
Some tips and techniques that I've picked up over the years. I don't maintain that these are 'official' or 'absolute'...they are simply my preference and things that have worked for me. I would like to share them here, and leave you to make the decision as to whether or not they fit in your 'code arsenal' :)
Author: Charlie Griefer (CJ)
Views: 20,679
Posted Date: Friday, August 15, 2003
An introductory look at CFSCRIPT. Rules, some basic syntax, and a couple of examples of loops and conditional processing.
Author: Charlie Griefer (CJ)
Views: 26,178
Posted Date: Saturday, January 18, 2003
A silly but fun time-waster that you can easily include on your Web site. You might be surprised at how addicting it can become :)
Author: Charlie Griefer (CJ)
Views: 16,423
Posted Date: Thursday, May 29, 2003
You've finished the first Mad Libs tutorial, but you feel like there's something missing. Of course there is! You want to be able to save the final output to a database to let your visitors browse through other user's stories. Includes a bad-words filter for the more conservative among us :)
Author: Charlie Griefer (CJ)
Views: 13,199
Posted Date: Thursday, May 29, 2003
Have you ever wanted to display your
Author: Charlie Griefer (CJ)
Views: 19,315
Posted Date: Thursday, May 29, 2003
This tutorial picks up where the Dynamic Columns tutorial left off, showing you how to not only output your data in a specified number of columns, but how to do it while still publishing well formed HTML.
Author: Charlie Griefer (CJ)
Views: 14,812
Posted Date: Saturday, May 31, 2003
How to group cfquery output in order to effectively display relational database data. Includes an overview of how to output nested groups as well.
Author: Charlie Griefer (CJ)
Views: 17,411
Posted Date: Tuesday, June 17, 2003
Your site requires your visitors to log in. of course, some of your visitors are going to forget their passwords (ok, most will forget their passwords). You don't want them to have to send you an e-mail, and then wait for a response. They need immediate access.
This tutorial shows two methods by which you can accomodate them.
Author: Charlie Griefer (CJ)
Views: 15,874
Posted Date: Thursday, August 28, 2003
Yes, I know we're a ColdFusion site...but ColdFusion does not live in a vacuum. We have to know SQL, HTML, CSS...and sometimes...JavaScript! This tutorial focuses on using JavaScript (in lieu of cfform) to create client side form validation (and explains why writing your own is better than using
Author: Charlie Griefer (CJ)
Views: 33,351
Posted Date: Thursday, August 14, 2003
Manage text-based files on your server from any Web browser. Create a new file, edit a file, or delete a file. Can be a life saver if you're on the road, and find an error in some of your code that needs a quick fix.
Author: Charlie Griefer (CJ)
Views: 16,374
Posted Date: Tuesday, June 3, 2003
A nifty little custom tag that will allow you to save a visitor's clickstream through your site, as well as display it back to them (with links).
Did I really just say 'nifty'?
Author: Charlie Griefer (CJ)
Views: 14,034
Posted Date: Monday, June 16, 2003
It's been out there since ColdFusion 4.5...most of us have heard of it...few of us use it. Here are some compelling reasons why you should get into the habit of using the
Author: Charlie Griefer (CJ)
Views: 19,496
Posted Date: Thursday, May 29, 2003