Last month I blogged about using ColdFusion's Asynchronous Gateway. Today I want to show another example of an Enterprise Gateway: The DirectoryWatcher. The DirectoryWatcher gateway does what you imagine it would - it watches a directory. It lets you monitor additions, changes, and deletions from a directory.
So with that in mind - let's build a simple example. I'm going to create a simple DirectoryWatcher to examine a folder that my client is using to drop images into. The DirectoryWatcher will do two things:
- Check the extension of the image and delete it if it isn't a list of valid extensions. I don't want all image formats for example, but just the ones that will work on the web.
- Check the size of the image, and if it is too big, resize it.
Sounds simple, right? First off - how do we check the size of an image? I typically recommend Alagad's Image Component, but for this I wanted a free solution folks could download right away, so I used Image CFC.
As before - we will start off in the Event Gateway Instances page of your ColdFusion Aministrator. Create a new instance using the DirectoryWatcher type. For my instance, I created a CFC under web root called imagewatcher.cfc. For the config file I copied the example config file to web root and named it imagewatcher.cfg. I then added my gateway instance.
Let's start by taking a look at the config file. Again - if you copy from the samples folder (C:\CFusionMX7\gateway\config\directory-watcher.cfg), you only need to edit one line. Here is the file I used:
# # DirectoryWatcherGateway configuration file #
# The directory you want to watch. If you are entering a Windows path # either use forward slashes (C:/mydir) or escape the back slashes (C:\mydir). directory=c:/apache2/htdocs/images
# Should we watch the directory and all subdirectories too # Default is no. Set to 'yes' to do the recursion. recurse=no
# The interval between checks, in miliseconds # Default is 60 seconds interval=10000
# The comma separated list of extensions to match. # Default is * - all files extensions=*
# CFC Function for file Change events # Default is onChange, set to nothing if you don't want to see these events changeFunction=onChange
# CFC Function for file Add events # Default is onAdd, set to nothing if you don't want to see these events addFunction=onAdd
# CFC Function for file Delete events # Default is onDelete, set to nothing if you don't want to see these events deleteFunction=onDelete
Pay attention to the directory line. This tells the gateway what directory to monitor. I also made the gateway run a bit quicker then normal. The default interval was 60 seconds, but I changed it to 10. You may ask - why didn't I change the extensions? I want my watcher to "clean up" any non-image uploads, so by keeping the extensions setting to *, I can ensure my CFC will always run.
So far so good? Now let's take a look at the CFC:
<cfcomponent>
<cfset variables.imageExtensions = "jpg,gif,png">
<cffunction name="onAdd" output="false" returnType="void">
<cfargument name="CFEvent" type="struct" required="true">
<cfset var data = arguments.CFEvent.data>
<cfset var filename = data.filename>
<cfset var imageData = "">
<cfset var imagecfc = createObject("component", "image")>
<cfif not listFindNoCase(variables.imageExtensions, listLast(filename, "."))>
<cflog file="imagewatcher" text="Deleting #filename# since it wasn't an image.">
<cffile action="delete" file="#filename#">
<cfreturn>
</cfif>
<cfset imageData = imagecfc.getImageInfo("", filename)>
<!--- anything wrong? --->
<cfif structKeyExists(imageData, "errormessage")>
<cflog file="imagewatcher" text="Deleting #filename# because: #imageData.errormessage#.">
<cffile action="delete" file="#filename#">
<cfreturn>
</cfif>
<cflog file="imagewatcher" text="#filename# is an ok image.">
<!--- too big? --->
<cfif imageData.width gt 250>
<cflog file="imagewatcher" text="Resizing #filename# because width was #imageData.width#.">
<cfset imagecfc.scaleX("", filename, filename, 250)>
</cfif>
<cfif imageData.height gt 250>
<cflog file="imagewatcher" text="Resizing #filename# because height was #imageData.height#.">
<cfset imagecfc.scaleY("", filename, filename, 250)>
</cfif>
</cffunction>
</cfcomponent>
I mentioned earlier that the DirectoryWatcher gateway lets you monitor both adds, edits, and deletes from a folder. In our case we only need to monitor adds. Therefore my CFC has one function - onAdd. ColdFusion will pass a structure of data to the component that includes the filename. Once I get that I check the extension. If it isn't valid, I delete it.
I then use ImageCFC to get information about the image. If anything goes wrong, ImageCFC returns an error message, so I simply look for that and log it. Also note that I delete the file as well.
Next I check the width and height. If either is bigger than 250 pixels, I scale the image. (One thing that would be nice if is ImageCFC had one simple Scale function, so I can ask it to just ensure the image wasn't bigger in any dimension.)
That's it! If you try this at home, have fun by throwing some files in the folder and just watching them change. (Be sure to use copies!) I had some large images and thought it was cool to watch their file sizes shrink as soon as the gateway picked them up.
Of course this isn't the only thing you could have done. I could have created thumnnails instead of shrinking the original file. I could have made thumbnails and then moved the images into another folder. I could have added a watermark. You get the idea.
Archived Comments
I think i'll build a test for this, maybe create a CFWinClam Antivirus for CF Uploads.
Pretty easy, watch the directory using your Gateway, use cfexecute to fire off an Antivirus scan on a file that's been uploaded to the web root. This would potentially avoid the need to use a real-time AV scanner on the server.
If you've used a number of the commercial real time scanners like me, you may have experienced file-locking issues or performance problems.
I'm wondering how it handles temp files, file locks, etc?
OOps, I did it again! ClamWin, not Winclam. That's important to know if you are researching it... Winclam results in a bunch of suspicious looking sites plastered with banners and popups.
Banners and popups? How gauche.
(Now pardon me while I add some more Adsense love here... ;)
Ray,
VERY, *very* cool. However, I have one question: can I monitor multiple directories, and if so, can I fire off a different reaction for each one (directory one calls dirOneOnAdd or whatever)?
THAT would be the freaking coolest thing ever...or at least this weekend :)
Remember that each EG Instance has a config file. So you can easily monitor N directories with N EG instances.
Hi Ray,
I tested the DirectoryWatcher, I have one question. In onAdd or onChange function, if I tried to call a cfc that reside in other folder, it said that the cfc could not be found. I tried several ways, the only way to make the cfc call works, ONLY if I put the handler/listener cfc in the same folder of the cfc that I am trying to invoke.
Is this a bug?
You need to make a CF mapping I believe. If your mapping was stuff and your cfc was named doit, then you could then make an instance of stuff.doit.
Excellent tutorial. I was hopeful I could set up a DirectoryWatcher Gateway to log changes to files in a particular directory. For some reason after adding a DirectoryWatcher Gateway Instance in CF 10 Developer and having enabled ColdFusion Gateway Services on the Settings page when I try to start the instance I created I'm told "Unable to start event gateway instance: Event Gateway Service is not enabled". I tried searching on this but found little about a fix and nothing that helps me.
Hmm, honestly, I don''t know. I haven't used CF Event Gateways in - well - probably almost as long as this blog post is old. ;) No suggestions outside of ringing up support - but maybe someone else who commented here will know. Sorry.
RE: Unable to start event gateway instance - I have this problem, too. Check your Sandbox Security. If it's on, turn it off and try it, it will most likely start.
Yet another thing that is being blocked by Sandbox Security after the JVM 7.55 update. Solr collections are breaking, for me, as well.
---Unable to start event gateway instance---
this below link will help you to avoid this problem.
http://stackoverflow.com/qu...