Issue with coldfusion cffile delete

2.2k Views Asked by At

I have a page where a user can update a page with 4 images and some other fields. There is already an image uploaded for each file input and when they upload a new one I want to take the new image, upload it then if the upload was successful delete the old image. However I am getting this error.

File /var/www/mywebsite.com/Pics/Sunset3.jpg specified in action delete does not exist. 

The files does exist in that directory as per my ftp. These files are even being displayed on the page as part of the form as a reference for the user as to what image is already there. I've heard of not being able to process a file upload on the same page and you have to use a secondary file, but these images are uploaded on a previous visit to the page, so they are not being uploaded AND processed on the same page.

Here is the code I have. The variable tableName is correct, it is defined earlier in the code, plus the database reflects the correct values after an upload attempt, it just breaks at the delete.

<cfquery name="getPreviousImage" datasource="#Application.datasourceName#">
        SELECT 
            image1, image2, image3, image4
        FROM 
            #tableName#
        WHERE 
            RecordID = '#form.ID#'
</cfquery> 

<cfset oldImage1 = getPreviousImage.image1>
<cfset oldImage2 = getPreviousImage.image2>
<cfset oldImage3 = getPreviousImage.image3>
<cfset oldImage4 = getPreviousImage.image4>

<cfset image1 = getPreviousImage.image1>
<cfset image2 = getPreviousImage.image2>
<cfset image3 = getPreviousImage.image3>
<cfset image4 = getPreviousImage.image4>

<cfif #form.image1# NEQ ""> 

    <cffile action="upload" destination="#Application.filePath#Pics/" filefield="image1" nameconflict="makeunique">
    <cfif isDefined ("cffile.serverFile")>
        <cfset image1Place = #cffile.serverFile#> 
    </cfif>

    <cfif #getPreviousImage.image1# NEQ "" AND #image1Place# NEQ "">
        <cffile action="delete" file="#Application.filePath#Pics/#oldImage1#">
    </cfif>
</cfif>

<cfif #form.image2# NEQ ""> 

    <cffile action="upload" destination="#Application.filePath#Pics/" filefield="image2" nameconflict="makeunique">
    <cfif isDefined ("cffile.serverFile")>
        <cfset image2Place = #cffile.serverFile#> 
    </cfif>

    <cfif #getPreviousImage.image2# NEQ "" AND #image2Place# NEQ "">
        <cffile action="delete" file="#Application.filePath#Pics/#oldImage2#">
    </cfif>
</cfif>

<cfif #form.image3# NEQ ""> 

    <cffile action="upload" destination="#Application.filePath#Pics/" filefield="image3" nameconflict="makeunique">
    <cfif isDefined ("cffile.serverFile")>
        <cfset image3Place = #cffile.serverFile#> 
    </cfif>

    <cfif #getPreviousImage.image3# NEQ "" AND #image3Place# NEQ "">
        <cffile action="delete" file="#Application.filePath#Pics/#oldImage3#">
    </cfif>
</cfif>

<cfif #form.image4# NEQ ""> 

    <cffile action="upload" destination="#Application.filePath#Pics/" filefield="image4" nameconflict="makeunique">
    <cfif isDefined ("cffile.serverFile")>
        <cfset image4Place = #cffile.serverFile#> 
    </cfif>

    <cfif #getPreviousImage.image4# NEQ "" AND #image4Place# NEQ "">
        <cffile action="delete" file="#Application.filePath#Pics/#oldImage4#">
    </cfif>
</cfif>


<cfquery name="UpdateInfo" datasource="#Application.datasourceName#">
    UPDATE 
        #tableName#
    SET                  
        title = <cfqueryparam value="#form.title#" cfsqltype="CF_SQL_VARCHAR" maxlength="250">,
        <cfif #image1Place# NEQ ""> 
            image1 =    <cfqueryparam value="#image1Place#" cfsqltype="CF_SQL_VARCHAR" maxlength="250">,
        </cfif>
        <cfif #image2Place# NEQ ""> 
            image2 =    <cfqueryparam value="#image2Place#" cfsqltype="CF_SQL_VARCHAR" maxlength="250">,
        </cfif>
        <cfif #image3Place# NEQ ""> 
            image3 =    <cfqueryparam value="#image3Place#" cfsqltype="CF_SQL_VARCHAR" maxlength="250">,
        </cfif>
        <cfif #image4Place# NEQ ""> 
            image4 =    <cfqueryparam value="#image4Place#" cfsqltype="CF_SQL_VARCHAR" maxlength="250">,
        </cfif>
        body = <cfqueryparam value="#form.body#" cfsqltype="CF_SQL_VARCHAR">
    WHERE RecordID = <cfqueryparam value="#form.ID#" cfsqltype="CF_SQL_INTEGER" maxlength="50">
</cfquery>

EDIT: This is the new error after I implemented the fileExist(). Also, I see all of these images building up on my server. I'm up to around sunset10.jpg among other photos. If I get this fileExist to work wouldn't all it do is prevent the error from being displayed and the delete will never execute. Because these files definitely do exist, unless I'm pointing to the wrong location of course.

Invalid CFML construct found on line 107 at column 138.
ColdFusion was looking at the following text:

>

The CFML compiler was processing:

An expression beginning with #, on line 107, column 23.This message is usually caused     by a problem in the expressions structure.
A cfif tag beginning on line 107, column 18.
A cfif tag beginning on line 107, column 18.
A cfif tag beginning on line 107, column 18.

I unfortunately cannot turn on robust exception reporting. Also, this error is displayed when I first navigate to the page. Before I even get a chance to submit the form. Meanwhile this code is inside an if which should only execute after the form has been filled out.

3

There are 3 best solutions below

0
On

I don't think file permissions are the issue as some have suggested. Permission errors will mention something about permissions. So I would assume that the error is telling the truth - that the file does indeed "not" exist. carefully check for case sensitivity (/Pics vs. /pics for example). Use Fileexists as suggested (your second error is likely a syntax error.

Turn on full robust debugging on your test site and you'll get more information. You might have done that and pared it down for this group.

You have some poorly used pound signs as well

<cfif #form.something# EQ "">

Pound signs are actually germane to output not evaluation. So this is better

<cfif form.something EQ "">

Finally - to make sure the code can get a handle to the file it's trying to delete you might consider named locks like so:

<cfif trim(form.image4) IS NOT ""> 

    <cflock name="piclock_img4" timeout="25">
    <cffile action="upload" destination="#Application.filePath#Pics/" filefield="image4" nameconflict="makeunique">
    <cfif structkeyexists(cffile,"serverfile")>
        <cfset image4Place = cffile.serverFile> 
    </cfif>
    </cflock>

    <cflock name="piclock_img4" timeout="10">
    <cfif trim(getPreviousImage.image4) IS NOT "" AND trim(image4Place) IS NOT "">
        <cffile action="delete" file="#Application.filePath#Pics/#oldImage4#">
    </cfif>
    </cflock>
</cfif>

IF that doesn't work you might try adding a sleep(2000) in between them - to give the OS time to release the file handle. These days with so many different kinds of storage it's hard to know the underlying I/O mechanism and how it trickles back up to the OS and your Code.

Hope this helps.

2
On

You can add a fileExists() clause to your IF statement before attempting the Delete. From your code it looks like you are assuming that the file exists based on what comes back from the DB not based on an actual lookup in the filesystem.

I would also make sure that you are not running into file permissions or casing issues (looks like you are on *nix environment)

1
On

Unfortunately, I may not have the definitive answer for you. It sounds like your server may be misconfigured, but I can't really tell. It's also possible that your file attributes (ok, the "mode" on linux/unix) are set wrong, so perhaps CF cannot see or delete it. Check into the mode="" attribute on your cffile commands.

Here's how to solve the problem of throwing exceptions. Just add a check for fileExists().

<cfif getPreviousImage.image1 NEQ "" AND image1Place NEQ "" AND fileExists(application.filePath & "Pics/" & oldImage1>
    <cffile action="delete" file="#Application.filePath#Pics/#oldImage1#">
</cfif>

When you copy the file after the upload (I assume that code immediately follows your given snippet), it will overwrite whatever file is there. If the files have the same name, it will simply overwrite any existing file, so maybe you don't even need to do the delete.

One more note - something vitally important - your query:

    SELECT 
        image1, image2, image3, image4
    FROM 
        #tableName#
    WHERE 
        RecordID = '#form.ID#'

You really, really need to put #form.ID# in a cfqueryparam. Your database can very easily be hacked with that. Here is what you need:

    WHERE 
        RecordID = <cfqueryparam value="#form.ID#" cfsqltype="cf_sql_integer" />

Also, I hope #tableName# isn't something that comes from a user input. It's not scoped, so if someone adds &tablename=foo to the URL, it could get picked up instead of your existing tableName variable. I recommend you specify the scope, probably like #variables.tableName#. Or better yet, don't make SQL table names dynamic (unless you really have to).