| |
| |
|
The following tutorial guides you through the implementation
of a simple FTP client that takes advantage of the built-in
file transfer capabilities of ColdFusion MX to access FTP
servers—and of the Flash Tree component, part of the
Flash UI Components Set 2 available from the Macromedia
Exchange—to display and handle the data retrieved
by ColdFusion MX. |
Getting
the software and sample files
First make sure you have the required software and that
it is correctly installed:
|
| Next download
the sample files to follow along with this tutorial: |
|
| |
Now
create a folder at cf_webroot\com\aftershape\ftp
and unzip/expand the ftpSpider.cfc ColdFusion MX component
into the ftp directory.
Note:
The cf_webroot directory path depends on your ColdFusion MX
installation.
The flash_ftp.fla file is the finished Flash
graphic user interface (GUI). This file will help you follow
the tutorial if you'd like to study the finished project.
The flash_ftp_start.fla file contains
everything except the ActionScript code—which is
explained during this tutorial. Use this file if you'd
like to add the ActionScript
yourself as it is presented. |
How
the ftpSpider component works
All the tasks performed on the remote FTP server are handled
by the ftpSpider ColdFusion MX component. This component uses
the CFFTP tag, so make sure it has been enabled in the ColdFusion
Administrator.
The component is composed of two CFFUNCTION tags which define
its two methods. The first, named FetchDirectories,
handles all the files and directories listing operations.
The second, named FetchFiles,
handles transferring operations from the remote FTP server
to the temporary directory of your ColdFusion MX server.
To
make these methods available to the Flash GUI, which I
build in the second part of the tutorial, you need to set
the attribute
access to "remote" for both CFFUNCTION tags that
form the component. |
Using
the FetchDirectories method
To use the component's first method, gather the needed FTP
parameters using a succession of CFARGUMENTS tags. Each FTP
parameter is collected and sent to the ColdFusion component
by the Flash GUI, as you'll see later on. These parameters
are the main attributes required by the CFFTP tag: |
| |
<cfargument
name="ftpUsername" type="any" required="yes">
<cfargument name="ftpPassword" type="any"
required="yes">
<cfargument name="ftpServer" type="any"
required="yes">
<cfargument name="ftpPort" type="numeric"
required="yes">
<cfargument name="ftpPassivemode" type="boolean"
required="yes">
<cfargument name="ftpTimeout" type="numeric"
required="yes">
<cfargument name="ftpDirectory" type="any"
required="yes"> |
| |
| Once
you've defined all the FTP parameters, you can start connecting
to the remote FTP server using another CFFTP tag, with the
action attribute set to OPEN. You will benefit from the connection-caching
feature of ColdFusion, which automatically defines a connection
name on the connection attribute. This feature enables you
to reuse the same connection information on successive CFFTP
tags: |
| |
<cfftp
action="OPEN"
server="#ARGUMENTS.ftpServer#"
username="#ARGUMENTS.ftpUsername#"
password="#ARGUMENTS.ftpPassword#"
stoponerror="No"
passive="#ARGUMENTS.ftpPassivemode#"
port="#ARGUMENTS.ftpPort#"
connection="MyFTPConnection"
retrycount="3"
timeout="#ARGUMENTS.ftpTimeout#"> |
| |
From
the remote FTP server, retrieve all names and attributes of
the stored directories and files as a query object using the
LISTDIR action of the CFFTP.
After that, send this information to the calling Flash interface
using the CFRETURN tag. Once you've done this, you can close
the FTP connection to avoid multiple open connections each
time you process the ColdFusion component: |
| |
<cfftp
action="LISTDIR"
stoponerror="No"
passive="#ARGUMENTS.ftpPassivemode#"
name="FileList"
directory="#ARGUMENTS.ftpDirectory#"
connection="MyFTPConnection">
<cfftp action="CLOSE"
stoponerror="No"
passive="#ARGUMENTS.ftpPassivemode#"
connection="MyFTPConnection">
<cfreturn FileList> |
| |
Using the FetchFiles method
The second method of the component works almost the same
way as FetchDirectories.
The only relevant difference is that now, because the
connection has already been established, you will download
the file selected on the Flash GUI to your ColdFusion
MX temporary folder instead of retrieving a list of directories.
You can accomplish this task easily by setting the action
attribute of the CFFTP tag to GETFILE.
The user will be notified about the final result of the
download operation by the CFFTP.
In the code example below, the succeeded
variable—which is populated by assigning a value
of No to the stoponerror
attribute of the CFFTP tag—is subsequently returned
to the Flash GUI by the CFRETURN tag:
|
| |
<cfftp
action="GETFILE"
stoponerror="No"
passive="#ARGUMENTS.ftpPassivemode#"
localfile="#GetTempDirectory()##ARGUMENTS.ftpFileLocal#"
remotefile="#ARGUMENTS.ftpFileRemote#"
transfermode="AUTO"
failifexists="Yes"
connection="MyFTPConnection">
<cfreturn CFFTP.Succeeded> |
| |
Note: Because
of its single-threaded nature, it's always a good idea
to lock the CFFTP tag. However, a digression on locking
was beyond the scope of this tutorial. For the sake of
simplicity,
each FTP operation is not locked and performs on separate
CFFTP tags—even if you could, for example, open the
connection to the remote FTP server and download the desired
file in a single step. For more information on locking,
please
refer to Macromedia's TechNote, "ColdFusion
Locking Best Practices." |
Building
the Flash FTP GUI
Open the flash_ftp_start.fla file and take a second to
examine it. As you can see, all the elements appear on
the Stage
for you. The only blank frame is on the top layer, which
is named "actions."
There are several input text fields at the top right of
the Stage. The user enters the login information into these
text fields to connect to the remote FTP server. The PushButton Flash component labeled "Connect"
passes the values gathered from the text fields to the ConnectToFTP
function you are going to write in a few minutes. That function
is responsible for starting the whole application.
Below the PushButton component, there's another dynamic
text field named "FieldServerMsg_txt"; this is
where you display custom error messages and the status of
each FTP operation.
The left side of the Stage is where the Tree Flash component
resides. FtpBrowser_tree is responsible for displaying the
remote FTP server files. Each time a tree node is selected,
this component calls a function named GetSelection,
which is one of the Tree component's behaviors.
Select the blank top layer and open the Actions panel by
pressing F9 or selecting Window > Actions. Once it's
opened, make sure your Script pane is set to Expert Mode.
Start by including the NetService.as file, which contains
the classes required by Flash Remoting: |
| |
#include
"NetServices.as" |
| |
The
first function you implement is the one that changes the status
of the selected tree node from open to closed (and vice versa).
This function is only called when the selected node is a branch
that corresponds to a folder on the remote FTP server.
Note the use of suffixes such as _tn (which stands for "tree
node") and _tree. I recommend using suffix strings
like these whenever writing ActionScript because the code
completion
support built into Macromedia Flash MX displays code hints: |
| |
function
OpenNode(state) {
CurrentNode_tn.setIsOpen(state);
FtpBrowser_tree.refresh();
} |
| |
To
handle the returned data from the service call to the FetchDirectories
method of your ColdFusion component, set up a default responder.
The function will populate the Tree Flash component depending
on the query object received from the spiderFtp.cfc file.
First, it will check to see if an empty query has been returned.
When the query contains one or more records, it will loop
through them and add the tree branch icon (if the processed
record is a directory) or a leaf icon (if it is a file).
Each tree node will be labeled with the name of the column
returned by the ColdFusion component, while the path column
will define the node's data value.
Notice that both the name and path columns are standard
CFFTP query object columns. CurrentNode_tn is nothing but
the directory used as a base folder for each call to the
ColdFusion component. Once the user begins browsing the
tree, CurrentNode_tn will be replaced by the selected tree
node data value. To provide visual feedback to the user,
the selected branch status will be set to open each time
the result of the service call would have been processed—even
if the returning query object is empty: |
| |
function
FetchDirectories_Result(result) {
// If the query returns no record...
if (result.getLength() == 0) {
// ...directory
is empty. Display message and open the branch.
FieldServerMsg_txt.text
= "Directory is empty";
OpenNode(true);
} else {
//
Loop through query result...
for
(i=0; i<result.getLength(); i++) {
//
...if a directory is found...
if
(result.getItemAt(i).isDirectory == "YES") {
//
...add it as a branch.
FtpBrowser_tree.addNode(CurrentNode_tn,
new FTreeNode(result.getItemAt(i).name).setData(result.getItemAt(i).path).setIsBranch());
//
...and open parent branch
OpenNode(true);
FieldServerMsg_txt.text
= "";
}
else {
//
Else add it as a leaf...
FtpBrowser_tree.addNode(CurrentNode_tn,
new FTreeNode(result.getItemAt(i).name).setData(result.getItemAt(i).path));
//
...and open parent branch
OpenNode(true);
FieldServerMsg_txt.text
= "";
}
}
}
}
|
| |
To
handle returned errors from the FetchDirectories
service function, set up a simple responder that displays
the error messages in the FieldServerMsg_txt
dynamic text field on the Stage: |
| |
function
FetchDirectories_Status(result) {
// Print error messages
FieldServerMsg_txt.text = result.details;
} |
| |
The
default responder for the FetchFiles
service function is straightforward. The FTP connection
errors are handled by the FetchDirectories
service function error responder, which is very helpful.
In fact, users are able to browse the remote FTP server
and choose a file that they wish to download as long as
the login information they entered to call the FetchDirectories
function is correct—you don't have to worry about
connection errors.
The CFFTP variable, which you receive from the ColdFusion
components, returns a simple Boolean value depending on
the download process result. You only need to display a
status message on the FieldServerMsg_txt dynamic text field
to provide users with feedback on the process: |
| |
function
FetchFiles_Result(result) {
if (result) {
FieldServerMsg_txt.text
= "File has been downloaded to your ColdFusion temporary
directory.";
} else {
FieldServerMsg_txt.text
= "Error occurred while trying to download the selected
file.";
}
} |
| |
The
CreateRootNode function
is called only once by the PushButton Flash component. It
creates the root node of the Tree Flash component and makes
the first call to the FetchDirectories
service: |
| |
function
CreateRootNode() {
RootNode_tn = new FTreeNode("/").setData("/");
CurrentNode_tn = RootNode_tn;
CurrentNode_tn.setIsBranch();
FtpBrowser_tree.setRootNode(CurrentNode_tn);
// Retrieve directories list
DirectoryService.FetchDirectories(FieldUsername_txt.text,
FieldPassword_txt.text, FieldServer_txt.text, FieldPort_txt.text,
PassiveMode_cb.getValue(), FieldTimeout_txt.text, CurrentNode_tn.getData());
FieldServerMsg_txt.text = "Retrieving
files list...";
} |
| |
The
function called by the PushButton Flash component on the Stage
simply resets the message text field and calls the CreateRootNode
function I discussed above: |
| |
function
ConnectToFTP() {
// Reset messages window...
FieldServerMsg_txt.text = "";
// ...and call CreateRootNode function
CreateRootNode();
}
|
| |
The
GetSelection function
is responsible for the Tree Flash component's behavior. It
sets the value for CurrentNode_tn
with the current selected node on the Tree. Also, if the selected
node is a branch, it opens or closes depending on the branch's
current status. The function calls the FetchDirectories
service if it has no child nodes; otherwise it calls the FetchFiles
service if the selected node is a leaf that represents a file
on the remote FTP server: |
| |
function
GetSelection() {
// Define current selected node
CurrentNode_tn = FtpBrowser_tree.getSelectedNode();
// The following routine opens or closes
branches depending on their status or number of children
if (CurrentNode_tn.isBranch()) {
if (CurrentNode_tn.getNumChildren()
== 0) {
DirectoryService.FetchDirectories(FieldUsername_txt.text,
FieldPassword_txt.text, FieldServer_txt.text, FieldPort_txt.text,
PassiveMode_cb.getValue(), FieldTimeout_txt.text, CurrentNode_tn.getData());
FieldServerMsg_txt.text
= "Retrieving files list...";
} else {
if
(CurrentNode_tn.isOpen()) {
OpenNode(false);
}
else {
OpenNode(true);
}
}
// If a leaf has been
selected instead of a branch, start the download
} else {
DirectoryService.FetchFiles(FieldUsername_txt.text,
FieldPassword_txt.text, FieldServer_txt.text, FieldPort_txt.text,
PassiveMode_cb.getValue(), FieldTimeout_txt.text, CurrentNode_tn.getLabel(),
CurrentNode_tn.getData());
FieldServerMsg_txt.text
= "Downloading file. Please wait.";
}
}
|
| |
Last,
but not least, comes the initialization code. I usually
leave this task for the end of my ActionScript development
so that I'm sure all my functions have been loaded before
processing it.
The initialization code simply contains the URL to the
default gateway, the construction of the main Service Object,
and some customization methods of the assets on the main
Stage. Remember that the URL to your default gateway depends
on your installation. In this example, the URL points to
the default gateway on the stand-alone web server that comes
with ColdFusion MX: |
| |
if
(init == null) {
init = true;
// Set the default gateway
NetServices.setDefaultGatewayUrl("http://localhost:8500/flashservices/gateway");
// Make the Gateway connection
GatewayConnection_nc = NetServices.createGatewayConnection();
// Create Service Object
DirectoryService = GatewayConnection_nc.getService("com.aftershape.ftp.ftpSpider",
this);
// Set autohide to tree's scrollbar
FtpBrowser_tree.setAutoHideScrollBar(true);
// ::trick:: remove expander
symbol linking to an object that doesn't exist
FtpBrowser_tree.setExpanderSymbolName("null");
// Set default ftp settings
FieldPort_txt.text = 21;
FieldTimeout_txt.text = 50;
// Set tab index order
FieldServer_txt.tabIndex = 1;
FieldPort_txt.tabIndex = 2;
FieldUsername_txt.tabIndex =
3;
FieldPassword_txt.tabIndex =
4;
FieldTimeout_txt.tabIndex =
5;
FieldServerMsg_txt.tabEnabled
= false;
} |
| |
This
tutorial demonstrates how Rich Internet Applications can extend
current Internet applications by providing developers with
the tools they need to easily replicate common desktop software
user interactions and functionalities. You can use this example
as a starting point to build a more complete and sophisticated
FTP client, or as inspiration for more compelling applications. |
| |
|
| About the author |
| Edoardo
Zubler is Senior Multimedia Developer at The
Fantastic Corporation, a Swiss-based supplier of broadband
multimedia delivery software for data broadcasting over
managed bandwidth. Edoardo is responsible for research and
development of technologies to enable the seamless integration
of Fantastic's platform into the workflow of its own customers,
and the design and implementation of ColdFusion- and Macromedia
Flash-based server-side content management systems and client-side
applications for handheld and mobile devices, tablet PCs,
and set-top boxes. Edoardo is also a moderator on the FlashCFM
forums, member of the Devmx
team, and active user of several Italian discussion groups
related to Macromedia products. He also runs Aftershape.com,
his own personal website, where he showcases his "digital
oddities."
|
| |