Remote Synthesis
Search my blog:
Viewing By Entry / Main
Nov 14, 2006

FTPCFC - Component Wrapper for CFFTP Functions

So I recently had the need to use cfftp functionality and decided to write a component wrapper for the functionality. The component (which can be downloaded via the download link below) wraps all the functions available in cfftp, making you code easier to read and far less verbose. It also handles some basic logic for you to further simplify some of the functions. I tested against a couple of connections and it worked nicely (with one exception I will discuss in a bit), however it doesn't support things like transferMode, passive or proxyServer at this point in time.So how do you use the component? The following code will establish a connection, get the contents of the root directory, create a new directory, change to that directory, upload a file to that directory and finally close the connection.

<cfset ftpService = createObject("component","ftpService").init("ftp.myserver.com","username","password") />
<cfset qryContents = ftpService.listDir() />
<cfset ftpService.createDir('mynewdir') />
<cfset ftpService.changeDir('mynewdir') />
<cfset ftpService.putFile('ftpService.cfc') />
<cfset ftpService.close() />

As you can see, the code is *much* cleaner and easier to understand than the alternative which is I think 6 cfftp calls.

Now what didn't work? Well, the remove action in cfftp handles removing files and directories, but it failed every time I tried to remove a directory even though I could validate it existed and was empty. I would either get the directory is not found or a permissions error depending on how I formatted the path. Since this is my first time actually using cfftp (never had the need before), I am hoping someone out there can help me figure out the problem.

Anyway, hopefully this will be useful to other folks. I will also be adding it to my projects page shortly. If people are using it and the functionality is requested, I can be talked into supporting transferMode, passive and proxyServer (I respond well to itunes gift certificates ;).

Download the attachment.

Comments
Jerry
Can this FTPCFC be combined with Flash's FileReference API for uploading? The Flash FileReference seems to have a 100 megs file limit and I need to find a way around this problem. Any suggestions?


Brian Rinaldi
I am not familiar with the Flash FileReference API, so I couldn't tell you. You could integrate this with Flash/Flex, in fact one idea I had was to use it for a Flex-based ftp UI, but remember, since this is FTP you would be moving a file on the local server to a remote FTP location...this is not for uploading files from the client (though I am not sure if that is what you are asking). Nonetheless, 100MB is a rather large limit and would take a long time to upload (if it didn't crap out on you) no matter what method you choose...


todd sharp
Brian - nice work, you're always one step ahead ;) I was JUST thinking about doing something like this the other day!! I bet you could make a client tool in flex by combining this with some of the file upload utilities out there. That would definitely be unique - and something I think I'd use very often. Could this be combined with sftp?

You should post this up on RIAForge.


Critter
Does it allow you to execute raw commands and capture the return?


Brian Rinaldi
Critter, I am not quite sure what you mean...the component simply wraps pre-existing cfftp functionality.


Critter
My bad... yeah that doesn't exist in cfftp.. I saw ftp wrapper and got all excited :)


Jay McConathy
I am attemptin to use this on a cf8 box and I keep ketting an error just trying to run listDir

this error:

The value returned from the listDir function is not of type query.

I am currently just doing this

&lt;cfset ftpService = createObject(&quot;component&quot;,&quot;ftpService&quot;).init(&quot;ftp.myserver.com&quot;,&quot;username&quot;,&quot;password&quot;) /&gt;

&lt;cfset ftpService.open() /&gt;

&lt;cfset qryContents = ftpService.listDir() /&gt;

any thoughts?


Brian Rinaldi
Hmm...not sure. I am running it here on CF8 just fine. There is no need to call the open() function (in fact looking at it this probably could be private) as the init() function already opens the connection for you. However, calling that function doesn't seem to change anything on my end.

It could be the connection got lost somehow.


Julian
I'm having the same problem with CF8.

The code Jay has above throws me an error in getCurrentDir (which gets called from listDir).

The value of ftp return from &lt;cfftp action=&quot;getCurrentURL&quot; connection=&quot;variables.connection&quot; result=&quot;ftp&quot; /&gt; is an empty string which causes a &quot;     You have attempted to dereference a scalar variable of type class java.lang.String as a structure with members.&quot; error.

Again, the code is just

&lt;cfset ftpService = createObject(&quot;component&quot;,&quot;ftpService&quot;).init(&quot;ftp.myserver.com&quot;,&quot;username&quot;,&quot;password&quot;) /&gt;

&lt;cfset ftpService.open() /&gt;

&lt;cfset qryContents = ftpService.listDir() /&gt;

Any help would be greatly appreciated.


Jay McConathy
As Julian has sad above

this is still giving me an error

I am attempting to use this on a cf8 box and I keep getting an error just trying to run listDir

The value returned from the listDir function is not of type query.

I am currently just doing this

&lt;cfset ftpService = createObject(&quot;component&quot;,&quot;ftpService&quot;).init(&quot;ftp.myserver.com&quot;,&quot;username&quot;,&quot;password&quot;) /&gt;

&lt;cfset ftpService.open() /&gt;

&lt;cfset qryContents = ftpService.listDir() /&gt;

as shown in a couple of posts there are issues here just trying to figure out how to resolve them


Brian Rinaldi
Sorry for the delay in responding. I keep trying to replicate the issue but can't seem to do it. I don't see why it would be the ftp variable since it isn't used. In fact, you can just take it out for that method since it isn't necessary. Does that resolve anything?


Julian
For me the error is thrown in getCurrentDir() function, which is being called from listDir(), as it's the default argument.

Within getCurrentDir() the action getCurrentDir is performed which returns the variable ftp, which for me on CF8 is an empty string.

The error is thrown by the last line :
&lt;cfreturn ftp.returnValue /&gt;

Which gives
&quot;You have attempted to dereference a scalar variable of type class java.lang.String as a structure with members.&quot;

as ftp is [empty string].

Thanks again,
Julian


Brian Rinaldi
Julian, Keeping in mind that this is just a wrapper for straight cfftp calls, I would recommend you try doing the straight calls because I don't think your issue is in the component. If you see the docs (http://www.cfquickdocs.com/cf8/#cfftp.connfileanddir) it states &quot;The cfftp.returnValue variable provides the return value for...getCurrentDir&quot;. Thus, if the ftp variable is returning empty string then I suspect there is a different issue going on with the ftp connection.


Julian
Thanks Brian, I might have to just try doing the straight calls. Just so you know everything works perfectly for me in CF7.

I tried dumping out Variables.connection at the top of the getCurrentDir() function and compared the output from both.

In CF7 it is an object of org.apache.commons.net.ftp.FTPClient.

In CF8 it is a structure containing stuff like DefaultPort, LocalAddress, RemoteAddress, RemotePort, Status (211), User etc.

Does this sound right?


Pragnesh Vaghela
Brian I was able to replicate the errors reported by Jay and Julian on CF 8.0 (no updates or hot fixes applied).

No issues on CF 8.0.1


Write your comment



(it will not be displayed)