Skip to main content
Known Participant
January 23, 2010
Answered

cfftp upload folder with subfolders in a single operation.

  • January 23, 2010
  • 5 replies
  • 5687 views

Hi everybody,

I have a folder full of subfolders with images in them and I want to upload them to an ftp server. Is this possible to instruct coldfusion to upload this folder with all its sublfolders and contents?

Thank you in advance,

Yannis

    This topic has been closed for replies.
    Correct answer Adam Cameron.

    Regarding:

    How is it complicated?

    For me, the complicated part is sub-sub-sub-subdirectories and getting back along the same path you came in on without missing anything.  If this were my task, that's what I would see as the biggest challenge.


    The thing is, with recursion, one doesn't need to worry about any level of the hierarchy other than the "current" one, and then making the recursive call at the right point.  The act of recursing sorts out the rest of it automatically.

    One trap I have seen people getting bogged down with when attempted something recursive is that they try to hold the entire "end result" in their heads (like traversing the sub-sub-sub-dirs), whereas there's simply no need.  One only ever has to focus on getting one tier of that right, and then call the function itself again to process the next tier.  All the nesting is handled / processed automatically by the recursion.  I guess the trick is to be able to be clear in one's head what needs to be passed into the function - from any arbitrary point - to be fully capable of processing a single tier, and how to take that info and process it in such a way as to derive the information necessary for the next tier.

    I guess with this requirement one needs to pass in the base file system dir, as well as the base FTP dir.  Subsequent recursive "base" dirs can be contrived simply by appending a subdir's name onto the current base dirs and passing the result into the recursive function call.

    To save needing to use recursion, one could use the <cfdirectory> tag's inbuilt recursive functionality, then doing a flat iteration over that recordset, using the paths from each record to contrive the FTP subdir.  Then either creating the remote dir (if the current records is a dir), or FTPing the file (if it's a file).

    Not really the one-fell-swoop BKBK is promising, but surely... not that complicated (although I hesitate to suggest such notions now).

    --

    Adam

    5 replies

    Known Participant
    May 3, 2010

    Hello friends,

    I'm back again and it is for good this time. What I mean is that I found the time at last (AT LAST) to finish this image manipulation app of mine. I decided to fully implement Coldfusion without plugins. I really need to thank BKBK for the nice FTP solution but I guess that the solution I implemented is nice and clean as well.

    Just in order to thank you (all) about your contribution I provide the code here. I hope it proves to be usefull to some of you.

    There is much to be done though. I should implement error handling, cfthread maybe, any many-many other nice things, but for now that the functionality is ready I will leave it as is. Later maybe...

    (I translated the messages for greek to english for your convinience right now, so excuse me for the typos!)

    Thank you all (Adam, Dan, BKBK),

    Yannis

    <!--- -------------------------------------------------------------------
    Name:..................................photoConverter.cfm
    Author:................................Yannis Develekos
    Description:...........................Photos converting application
    Created:...............................23 Jan 2010
    Previous Modified:.....................N/A
    Last Modified:.........................N/A
    Previous Modification Description:.....N/A
    Last Modification Description:.........N/A
    -------------------------------------------------------------------- --->

    <!---
    Template logic:
      - Creates the watermark and loads it in memory  (code from http://www.bennadel.com/blog/775-learning-coldfusion-8-cfimage-part-iii-watermarks-and-transparency.htm - Thank you Ben)
      - Checks all the contents of a folder and loads the images one by one (cfdirectory action="list").
      - Opens ftp connection
      - Checks for the existense of the folder on the web server and if it does not exist it creates it
      - If it is a valid image, it applies the watermark after calculating position and dimensions
      - Uploads the images to the server
      - Uploads the xml file needed for the DB
      - Closes the ftp connection
      - Outputs statistics to the server
    --->

    <!--- Name the working directory --->
    <cfset imgDirectory = "C:\Inetpub\ftproot\images\" >

    <!--- Create the variable to hold number of photos processed --->
    <cfset photoNumber = 0 >

    <!--- Create the variable to hold number of images already existed --->
    <cfset imagesExisted = 0 >

    <!--- Let give the user something to read (cfflush) --->
    <!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>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Σύστημα Διαχείρισης Φωτογραφιών - Aptown Real Estate</title>
    </head>

    <body style="font:normal bold 12px/30px 'Trebuchet MS', Arial, Helvetica, sans-serif;">
    <h1 style="color:#339;">Image manipulation system - Aptown Real Estate</h1>
    <h2>System Initialization</h2>
    <h3 style="color:#339;">Please wait...</h3>
    <cfflush>

    <!---~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Create a new image for the watermark with the given
    dimensions and give it a very light gray canvas color.
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --->

    <cfset objWatermark = ImageNew(
    "",
    300,
    70,
    "rgb",
    "##F0F0F0"
    ) />

    <!---
    Set the drawing color. This will be the color
    for all image manipulations going forward.
    --->
    <cfset ImageSetDrawingColor(
    objWatermark,
    "black"
    ) />

    <!--- Draw the rectangle. --->
    <cfset ImageDrawRect(
    objWatermark,
    4,
    4,
    (objWatermark.GetWidth() - 8),
    (objWatermark.GetHeight() - 8)
    ) />

    <!---
    Set text drawing anti aliasing. This will give are
    text a smoother rendered look (rather than jagged
    text you would see on a web page).
    --->
    <cfset ImageSetAntialiasing(
    objWatermark,
    "on"
    ) />

    <!--- Create text attributes. --->
    <cfset objAttributes = {
    Font = "Arial",
    Size = "60",
    Style = "Bold"
    } />

    <!--- Draw the watermark text onto our watermark image. --->
    <cfset ImageDrawText(
    objWatermark,
    "APTOWN",
    15,
    58,
    objAttributes
    ) />

    <!--- save watermark width and height for later use --->
    <cfset watermarkWidth = objWatermark.GetWidth() />
    <cfset watermarkHeight = objWatermark.GetHeight() />

    <!---~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         Watermark created!
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --->

    <!--- use cfdirectory to create photos list --->
    <cfdirectory
    action="list"
    directory="#imgDirectory#"
    recurse="yes"
    name="ourPhotos" />

    <!--- open ftp connection to the web server --->
    <cfftp action = "open"
        username = "aptown"
        connection = "ftpToApollo"
        password = "Asdf1234"
        server = "ftp.microelements.gr"
        stopOnError = "Yes">

    <!--- loop over the contents of cfdirectory --->
    <cfloop query="ourPhotos">

    <!--- Is it a folder? --->
    <cfif ourPhotos.type IS "Dir">

      <!--- Is it available on the web server? --->
      <cfftp action="existsdir" connection="ftpToApollo" directory="/images/photos/#ourPhotos.name#">

      <!--- If it's not, create it now --->
      <cfif cfftp.returnValue eq 'no'>
       <cfftp action="createdir" connection="ftpToApollo" directory="/images/photos/#ourPhotos.name#">
      </cfif>
     
      <!--- and save it to a variable to use it in subsequent file operation in the same folder --->
      <cfset currentFolder = "#ourPhotos.name#" >

    <!--- if it was not a folder this should be an image --->
    <cfelse>
      <!--- setup the correct filename now and --->
      <!--- get rid of the invalid characters in the image name --->
      <cfset photoName = LCase(REReplaceNoCase("#ourPhotos.name#","[^0-9A-Za-z\.\,]","i","ALL")) />

      <!--- maybe it has already been uploaded to the server --->
      <!--- to find out check for a thumb_imagename.jpg existense --->
      <cfif FileExists("#ourPhotos.directory#\thumb_#photoName#") OR Find("thumb_","#ourPhotos.name#") OR Find("large_","#ourPhotos.name#") >

       <!--- update imagesExisted variable and move to the next ourPhotos entry --->
       <cfset imagesExisted = imagesExisted + 1 / 3>
      <cfelse>

       <!--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          run the image manipulation process
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --->

       <!--- Is it a valid image? --->
       <cfif IsImageFile("#ourPhotos.directory#\#ourPhotos.name#")>

        <!--- Create a ColdFusion image from an existing JPEG file. --->
        <cfimage source="#ourPhotos.directory#\#ourPhotos.name#" name="myImage" />
       
        <!--- Turn on antialiasing to improve image quality. --->
        <cfset ImageSetAntialiasing(myImage,"on")>
       
        <!--- create the large photo --->
        <cfset ImageScaleToFit(myImage,420,"","highestQuality") />
        
        <!---
        When we paste the watermark onto the photo, we don't
        want it to be fully visible. Therefore, let's set the
        drawing transparency to 60% before we paste.
        --->
        <cfset ImageSetDrawingTransparency(
         myImage,
         60
         ) />
     
        <!---
        Paste the manually created watermark image onto the photo.
        This time, we don't need to turn on any anti aliasing since
        the watermark has solid borders. The anti alisasing only
        helps us when the pasted image has anti aliased perimeter.
        --->
        <cfset ImagePaste(
         myImage,
         objWatermark,
         ((myImage.GetWidth() - watermarkWidth) / 2),
         ((myImage.GetHeight() - watermarkHeight) / 2)
         ) />
       
       
        <!--- and write it to a file --->
        <cfimage source="#myImage#" action="write" destination="#ourPhotos.directory#\large_#photoName#" overwrite="true" />
       
        <!--- upload the image to the web server --->
        <!--- The image will be put into the specified directory of the FTP server  --->
        <cfftp 
         connection = "ftpToApollo"
         action = "putFile" 
         name = "uploadImage" 
         transferMode = "binary" 
         localFile = "#ourPhotos.directory#\large_#photoName#" 
         remoteFile = "/images/photos/#currentFolder#/large_#photoName#"
         failifexists="no">
        
        <!--- create the thumb --->
        <cfset ImageScaleToFit(myImage,50,"","highestQuality") >

        <!--- and write it to the disk too --->
        <cfimage source="#myImage#" action="write" destination="#ourPhotos.directory#\thumb_#photoName#" overwrite="true" />

        <!--- and upload it too --->
        <cfftp 
         connection = "ftpToApollo"
         action = "putFile" 
         name = "uploadThumb" 
         transferMode = "binary" 
         localFile = "#ourPhotos.directory#\thumb_#photoName#" 
         remoteFile = "/images/photos/#currentFolder#/thumb_#photoName#"
         failifexists="no">

        <cfset photoNumber = photoNumber + 1 />
        <cfoutput>#photoNumber# - #photoName#</cfoutput><br />
       
        <!--- Let's flush output to the user --->
        <cfflush>
       <cfelse>
        <!--- let the user know that we have an invalid image --->
        <hr />
        <cfoutput>
         <h3 style="color:red;">ΑΤΤΕΝΤΙΟΝ! The file #ourPhotos.directory#\#ourPhotos.name# is not a valid image!</h3>
        </cfoutput>
        <hr />
        <cfprocessingdirective suppresswhitespace="no">
       
         <!--- Send mail to me now --->
         <cfmail
          subject="Problem with aptown image file process"
          from='"Aptown Administrator" <johnny@aptown.eu>'
          to="john@microelements.gr"
          cc="podaras@aptown.eu"
          type="html">
          <h1>The image manipulation system reports an error</h1>
          <h2>Problem with image file</h2>
          <h3>The file #ourPhotos.directory#\#ourPhotos.name# is not a valid image!</h3>
         
         </cfmail>
        </cfprocessingdirective>
       </cfif>
      </cfif>
    </cfif>
     
    </cfloop>

    <!--- compress the xml file --->
    <cfzip
    source="#imgDirectory#/orbit.xml"
        file = "#imgDirectory#/orbit.zip"
        action = "zip"
        overwrite = "yes"
        recurse = "no">

    <!--- and upload it to the server --->
    <cfftp 
    connection = "ftpToApollo"
    action = "putFile" 
    name = "uploadXML" 
    transferMode = "binary" 
    localFile = "#imgDirectory#\orbit.zip" 
    remoteFile = "/upload/orbit.zip"
    failifexists="no">

    <!--- we do not need the ftp connection any more --->
    <cfftp action="close" connection="ftpToApollo">

    <!--- some nice statistics for the user --->
    <h1 style="color:#339;">Statistics:</h1>

    <!--- number of images uploaded --->
    <cfswitch expression="#photoNumber#">
    <cfcase value="0">
      No images needed to be uploaded to the web server!<br />
    </cfcase>
    <cfcase value="1">
      <cfoutput><span style="font-size:large;">1 image</span> uploaded to the web server!</cfoutput><br />
    </cfcase>
    <cfdefaultcase>
      <cfoutput><span style="font-size:large;">#photoNumber# images</span> uploaded to the web server!</cfoutput><br />
    </cfdefaultcase>
    </cfswitch>

    <!--- number of images already on the server --->
    <cfswitch expression="#imagesExisted#">
    <cfcase value="0">
      No images already available on the web server!<br />
    </cfcase>
    <cfcase value="1">
      <cfoutput><span style="font-size:large;">1 image only</span> found on the web server!</cfoutput><br />
    </cfcase>
    <cfdefaultcase>
      <cfoutput><span style="font-size:large;">#imagesExisted# images</span> already existed on the web server!</cfoutput><br />
    </cfdefaultcase>
    </cfswitch>

    </body>
    </html>

    Known Participant
    May 3, 2010

    The code disappeared!!!

    Here is a link to the file!

    Known Participant
    February 11, 2010

    Jentlemen,

    I just wanted to point out the fact that I never finished this. So this is the reason why I did not provide you with feedback so far. Anyway I will have to complete this project any day soon and I will let you know about the results.

    Thank you all,

    Yannis

    BKBK
    Adobe Expert
    February 11, 2010

    Very kind of you to update us. Much appreciated.

    BKBK
    Adobe Expert
    January 27, 2010

    @Yannis Develekos

    cfftp upload folder with subfolders in a single operation.

    Was my last post a possible solution?

    Known Participant
    January 27, 2010

    I will get back on this project on Monday. Of course whatever the solution will be I will let you know. I never forget to do that .

    Thanks,

    Yannis

    Known Participant
    January 24, 2010

    Gentlemen,

    Please do not turn this into a fight. You seem to be both experienced developers willing to help anyone who seeks for help. I have to say that the information you both provide is really helpful for me in some way. And it proves that you are both right at the same time so there is really not need to argue.

    For example, the solution suggested is not really “complicated”. I agree. But I also agree with the statement that if there is a simpler solution available, so this might prove to be complicated indeed. You see, you are both correct at the same time.

    Allow me to say that in order to provide helpful answers it is important for us to react calmly and gently.

    Thank you all for your ideas so far. It seems that we will indeed come up with a way to accomplish this really easily.

    Yannis

    Inspiring
    January 24, 2010
    Please do not turn this into a fight.

    There is no fight. BKBK has a habit of being unnecessarily obtuse; I have a habit of calling him/her on it.  BKBK reacts to that.  I roll my eyes in despair and move on (OK, sometimes not immediately ;-).

    Of course you're right in that I should not rise to the original obtuse... err... -ness.  Obtusitude.  Obtusicity.  Whatever.  It's a pointless waste of time, and a bit mean-spirited of me.  Oh well.

    At the end of the day the problems usually get solved, which is the main thing.  We just provide a sideshow too ;-)

    --

    Adam

    BKBK
    Adobe Expert
    January 23, 2010

    Yes, it is possible to use Coldfusion's cfftp tag to create directories on the FTP server, and then upload the files into them. However, the code is likely to be complicated, as subfolders imply recursion.

    Why bother with Coldfusion?  FileZilla is specialist FTP software that would do it for you, as easy as one, two, three, leaving you ample time for beer. Furthermore, it is open-source and free.

    If FileZilla makes you happy, think of the poor bugger burning the candle at both ends while others are having their beer. Go to the site and donate.

    Known Participant
    January 23, 2010

    Thanks friend,

    This was the answer of a wise man. But I have a couple of points to make:

    1. I new that it was complicated and that's why I did not want to implement it that way. I hoped that I missed something and there was a simple way to copy the complete folder structure along with contents in a simple step.
    2. I wish to give the client a completely transparent experience so that he will not need to use an ftp client as you suggested. But that's what I may end up doing.

    Thanks a lot and I still hope that anything else comes up that would give me the first option.

    Thanks...

    Inspiring
    January 23, 2010

    To get files from a client to your server, cffile is the tag you want, not cfftp.