Working on a page that displays the Standard Operating Procedures (SOP) and allows a non-Admin to download the SOP in a PDF file. Clients decided they didn't want admins to be limited to uploading just PDF files, and they want the option to upload .doc and .docx files. I need the download link to produce a PDF though.
Right now uploading either a .doc/.docx or a .pdf will display as I want it to using the Google Viewer. But when I attempt to download the test file it cant be opened if the files uploaded was a .doc/.docx. I've looked over this, and I'm sure I'm missing something stupid.
I'm using cfdocumnet as was suggested on another question.
Download link:
<cfoutput>
<li class="active">
<ahref="/files/SOP/SOP.pdf"
download="SOP.pdf" target="_blank">Download SOP</a>
</li>
</cfoutput>
Check for Admin (variable is created elsewhere) and form to upload file:
<cfif isAdmin>
<h3>Upload New SOP</h3>
<cfparam name="form.fileUpload" default="">
<cftry>
<cffile action="upload"
fileField="fileUpload"
destination="#expandPath('.')#\files\SOP\"
accept="application/pdf,
application/msword,
application/vnd.openxmlformats-officedocument.wordprocessingml.document,
application/x-tika-msoffice"
nameconflict="OVERWRITE">
<cfset fileName=CFFILE.serverfile>
<cfdocument
format="PDF"
srcfile="#expandPath('.')#\files\SOP\#fileName#"
filename="#expandPath('.')#\files\SOP\SOP.pdf"
overwrite="YES">
</cfdocument>
<p>Thank you, your file has been uploaded.</p>
<cfoutput>#fileName#</cfoutput>
<form enctype="multipart/form-data" method="post">
<input type="file" name="fileUpload"/>
<input type="submit" name="submit" value="Upload File"/>
</form>
<cfcatch type="any">
<!--- file is not written to disk if error is thrown --->
<!--- prevent zero length files --->
<cfif FindNoCase("No data was received in the uploaded", cfcatch.message)>
<p>No data was received in the uploaded file.</p>
<!--- prevent invalid file types --->
<cfelseif FindNoCase("The MIME type or the Extension of the uploaded file", cfcatch.message)>
<p>Invalid file type. Please upload file as a PDF or Word Doc</p>
<!--- prevent empty form field --->
<cfelseif FindNoCase("did not contain a file.", cfcatch.message)>
<p>Please seclect a PDF to upload.</p>
<!---all other errors --->
<cfelse>
<p>Unhandled File Upload Error</p>
<cflog type="Error" file="#application.applicationname#_dcnsopupload_error" text="#cfcatch.Message# - #cfcatch.Detail#" />
<cfoutput>#cfcatch.Detail#</cfoutput>
</cfif>
</cfcatch>
</cftry>
</cfif>
And on a side note, because I want the downloadable .pdf to be the given name "SOP.pdf" is there a way I can delete the user-uploaded file after renaming it and converting it? Just so there aren't 30 different outdated SOP documents on the server.
The form upload code looks wrong. Due to the cfparam, it's probably trying run the upload/conversion before the form is even submitted. Remove the cfparam and use structKeyExists() to verify the file field was submitted before trying to process it.
Try a simplified example first, with only the form and upload code (no error handling).
<!--- If file was uploaded, process it --->
<cfif structKeyExists(FORM, "fileUpload")>
<cffile action="upload"
fileField="fileUpload"
destination="#GetTempDirectory()#"
accept="application/pdf,application/msword,
application/vnd.openxmlformats-officedocument.wordprocessingml.document,
application/x-tika-msoffice"
nameconflict="makeunique">
<cfset savedFilePath = cffile.serverDirectory &"/"& cffile.serverFile>
<!---
more file validation here ...
--->
<!--- convert file --->
<cfdocument format="PDF"
srcfile="#savedFilePath#"
filename="#expandPath('.')#\files\SOP\SOP.pdf"
overwrite="YES">
</cfdocument>
<!--- cleanup temp file --->
<cfif fileExists(uploadedFile)>
<cfset fileDelete(uploadedFile)>
</cfif>
<!--- Otherwise, just display the form --->
<cfelse>
<form enctype="multipart/form-data" method="post">
<input type="file" name="fileUpload"/>
<input type="submit" name="submit" value="Upload File"/>
</form>
</cfif>
Also, though a bit dated, some of these tips on securing file uploads are still valid (such as not uploading files to the web root):
http://www.learncfinaweek.com/week1/File_Uploads/
https://www.petefreitag.com/item/701.cfm
Related
I am fairly new to Coldfusion and I am having a hard time to figure out how to allow users to upload images in my form
Currently, I was able to find the following code that will upload the image in Coldfusion:
<cfparam name="form.fileUpload" default="">
<cfif len(trim(form.fileUpload))>
<cffile action="upload"
fileField="fileUpload"
destination="C:\docs">
<p>Thankyou, your file has been uploaded.</p>
</cfif>
<form enctype="multipart/form-data" method="post">
<input type="file" name="fileUpload" /><br />
<input type="submit" value="Upload File" />
</form>
Although it gives me an idea on how to approach my issue, I still not sure on the following:
Instead of the destination="C:\docs" storing the file at a drive, I would like to be able to upload the uploaded image to an email. The reason being is the once the user finishes and submits the form, an email will be sent out to the user who submitted the request and the user who will be assigned in creating the card.
How can I achieve this? Any suggestions and examples would be greatly appreciated
Use CFMAILPARAM with remove="yes". You can also show the filename in the email. Full example:
<cfmail
to="the#recipient.com"
subject="#application.companyName# Contact Submission"
type="html"
from="#form.email#">
<!--- WAS A FILE UPLOADED? --->
<cfif isDefined("form.attachment") and form.attachment neq ''>
<!--- SAVE THE FILE --->
<cffile action="upload" fileField="attachment" destination="#expandPath('./')#" nameConflict="Overwrite">
<!--- ATTACH TO EMAIL, THEN DELETE IT --->
<cfmailparam
disposition = "attachment"
file = "#expandPath('./' & cffile.serverfile)#"
remove = 'yes'>
<!--- MAKE THE FILE NAME A FORM FIELD --->
<cfset form.attachment = cffile.serverfile>
</cfif>
<html>
<body>
<cfset form.URL = cgi.http_referrer>
<table border="1" cellspacing="0" cellpadding="3">
<tr>
<th>Field</th>
<th>Value</th>
</tr>
<cfloop list="#form.fieldnames#" index="f">
<tr>
<td nowrap="nowrap" valign="top">#replace(f, '_', ' ', 'all')#</td>
<td>#form[f]#</td>
</tr>
</cfloop>
</table>
</body>
</html>
</cfmail>
I have been allowing user to upload file via a form online like this:
<form action="upload.htm" enctype="multipart/form-data" name="upload_form" method="post">
<input type="file" name="upfile">
<input type="submit" name="upload" value="Upload Photos">
</form>
Then on the back end the cffile would upload the file:
<cffile action="upload" destination="#currentpath#" accept="image/jpeg, image/gif, image/png" fileField="form.upfile" nameconflict="makeunique">
Now I want to do automation so that the image file does not have to be sitting in the user's computer but rather from a web destination e.g. http://www.sample.com/images/myimage.jpg instead of c:/images/myimage.jpg
I have tried this:
<cfhttp method="get" url="http://www.example.com/images/myimage.jpg" resolveurl="Yes" throwOnError="Yes">
<cfif cfhttp.mimeType eq "image/jpeg">
<cfset currentpath = expandpath('/test/')>
<cffile action="upload" destination="#currentpath#" accept="image/jpeg, image/gif, image/png" fileField="#cfhttp.fileContent#" nameconflict="makeunique">
</cfif>
However, it is giving me an error:
Invalid content type: ''. The files upload action requires forms to
use enctype="multipart/form-data"
I am not using a form to upload but it seems to require a form field.
So I tried this:
<cfhttp method="get" url="http://www.example.com/images/myimage.jpg" resolveurl="Yes" throwOnError="Yes">
<cfif cfhttp.mimeType eq "image/jpeg">
<cfset currentpath = expandpath('/test/')>
<cffile action="write" output="#cfhttp.fileContent#" file="#currentpath#/someimage.jpg">
</cfif>
This one writes out a "file" called someimage.jpg but the output is NOT a jpg file, but something unrecognizable. With the cffile "write", it doesn't allow to check for image type or same file name.
Any help is appreciated. Thank you in advance.
Assuming the call was successful, the current code may be retrieving and/or saving the response as text, instead of binary, which would corrupt the image. To ensure you get back a binary image, use getAsBinary="yes".
Having said that, it is simpler to save the response to a file directly within the cfhttp call:
<cfhttp url="http://www.example.com/images/myimage.jpg"
method="get"
getAsBinary="yes"
path="#currentpath#"
file="someimage.jpg"
resolveurl="Yes"
throwOnError="Yes" />
This code used to work fine with ColdFusion 9:
<cfif form.btnSaveImage EQ "Upload">
<cftry>
<cffile accept="image/*" action="upload" destination="C:\T" filefield="vcImageFile" nameconflict="overwrite">
<cfcatch type="Any">
<cfdump var="#cfcatch#" label="cfcatch">
</cfcatch>
</cftry>
</cfif>
<form action="<cfoutput>#cgi.SCRIPT_NAME#</cfoutput>" method="post" name="frmImagesAdd" id="frmImagesAdd" enctype="multipart/form-data">
<label for="vcImageFile">Image File*:</label><br>
<input type="file" name="vcImageFile" id="vcImageFile" size="40" maxlength="255" message="Image file is required to upload."><br>
<input type="hidden" name="btnSubmit" value="Add Image">
<input name="btnSaveImage" type="submit" value="Upload">
</form>
The issue is the accept="image/*" attribute of the cffile tag. In ColdFusion 9, this wildcard used to accept any file with a MIME type that started with "image/". In ColdFusion 10, I get the following error message:
"The MIME type or the Extension of the uploaded file image/jpeg was
not accepted by the server."
The MimeType value is "image/jpeg", so it should work with the wildcard.
I looked at the wikidocs for the cffile tag and it says this:
ColdFusion 10: Modifications to the attribute accept}}
However, it doesn't elaborate what those modifications are!
Looking at the upload action docs, it says that it will accept a comma delimited list of mime types. Does this mean that wildcards are no longer accepted?
FYI, this blog post has indicated the same problem same time last year:
http://www.cutterscrossing.com/index.cfm/2013/2/21/ColdFusion-10-File-Uploads-and-MIME-Types
Now, I remembered that there were changes to MIME type checking in CF
10, but I wasn't entirely sure. This code, written by one of our
developer's several months ago, would allow any "image" MIME type.
What we discovered is that we couldn't do this kind of wildcard
mapping under CF 10, that we now had to list out each accepted MIME
type as a comma delimited list.
I have a checkbox inside of a form tag and I basically want to persist the checked state of the checkbox using a session. Apparently I'm doing it wrong because whenever I reload the page it sets the session back to off(which is the default value for the checkbox param). Here is the code i'm using.
Form:
<cfform name="matureContent" method="post" action="/index.cfm?fuseaction=main.Channels_Detail&c=#URL.c#" enctype="multipart/form-data">
<cfif SESSION.matureSession eq "on">
<input name="myCheckbox" type="checkbox" checked="checked" />
<cfelse>
<input name="myCheckbox" type="checkbox"/>
</cfif>
<input type="submit" value="Save" />
</cfform>
Session variable and params if they are not present on page load.
<cfparam name="form.myCheckbox" default="off">
<cfparam name="SESSION.maturesession" default="off">
<cfset SESSION.maturesession = form.myCheckbox>
If i'm going about this completely the wrong way let me know. Thanks.
<cfparam name="form.myCheckbox"
default="off">
<cfparam name="SESSION.maturesession"
default="off">
<cfset SESSION.maturesession =
form.myCheckbox>
I think that will result in overwriting the saved value if you return to the page from somewhere else. Instead, try updating the session value only when the form was submitted. Also, since you are using a cfform you could shortcut things by using yes/no instead of on/off.
Update I forgot the cfparam for the session variable. But if you truly want to carry it throughout the session, you could also initialize it onSessionStart instead.
<cfparam name="SESSION.maturesession" default="no">
<cfif structKeyExists(FORM, "submit")>
<cfparam name="form.myCheckbox" default="no">
<cfset SESSION.maturesession = form.myCheckbox>
</cfif>
<cfform name="test" method="post" ....>
<cfinput name="myCheckbox" type="checkbox" value="yes" checked="#session.matureSession#" />
<input type="submit" name="submit" value="Save" />
</cfform>
Do you actually have sessions turned on?
You have to explicitly turn on sessions for your app using the CFAPPLICATION tag if you're using Application.cfm, or, if you're using Application.cfc, by setting this.sessionManagement = true.
Application.cfm:
<cfapplication
name = "application name"
applicationTimeout = #CreateTimeSpan(0,2,0,0)#
sessionManagement = "yes"
sessionTimeout = #CreateTimeSpan(0,0,20,0)#>
Application.cfc:
<cfcomponent output="false">
<!--- Application name, should be unique --->
<cfset this.name = "ApplicationName">
<!--- How long application vars persist --->
<cfset this.applicationTimeout = createTimeSpan(0,2,0,0)>
<!--- Should we even use sessions? --->
<cfset this.sessionManagement = true>
<!--- How long do session vars persist? --->
<cfset this.sessionTimeout = createTimeSpan(0,0,20,0)>
</cfcomponent>
OK, then if sessions ARE turned on, when you submit the form, what does the code look like that you're posting the form to?
How can I get the filename of a file before I call the
<cffile action = "upload">
? I can get the filename of the temp file, but not of the actual filename. In PHP land I can use the $_FILES superglobal to get what I want - but as far as I can tell no such thing exists in ColdFusion.
I can get the filename client-side but would really want to do this server side.
Thanks
Yes this is possible. You can use this function to grab the client file name before using the cffile tag:
<cffunction name="getClientFileName" returntype="string" output="false" hint="">
<cfargument name="fieldName" required="true" type="string" hint="Name of the Form field" />
<cfset var tmpPartsArray = Form.getPartsArray() />
<cfif IsDefined("tmpPartsArray")>
<cfloop array="#tmpPartsArray#" index="local.tmpPart">
<cfif local.tmpPart.isFile() AND local.tmpPart.getName() EQ arguments.fieldName> <!--- --->
<cfreturn local.tmpPart.getFileName() />
</cfif>
</cfloop>
</cfif>
<cfreturn "" />
</cffunction>
More info here: http://www.stillnetstudios.com/get-filename-before-calling-cffile/
I'm using Railo and found the original filenames with:
GetPageContext().formScope().getUploadResource('your_file_input_form_name').getName();
maybe this works on an adobe server as well? its quite handy if you want to rename your uploaded file somehow and don't want it to get moved through two temp dirs (see Renaming Files As They Are Uploaded (how CFFILE actually works))
I don't know of a way to find out before calling cffile, but there may be a workaround.
When you call <cffile action="upload"> you can specify a result using result="variable". So, call the upload with the destination as a temp file. Your result variable is a struct which contains the member clientFile, which is the name of the file on the client's computer.
Now, you can use <cffile action="move"> to do whatever it is you need to do with the original filename.
WOW, i found a great and easy solution! with a little javascript
In this way you get the temp filename for the cffile upload and the actual file.jpg name for the database
<html>
<head>
<script type="text/javascript">
function PassFileName()
{
document.getElementById("fileName").value=document.getElementById("fileUp").value;
}
</script>
</head>
<body>
<form name="form1" method="post" enctype="multipart/form-data" >
File: <input type="file" name="fileUp" id="fileUp" size="20" onchange="PassFileName()" /> <br />
Title: <input type="text" name="Title" id="Title"><br />
<input type="hidden" id="fileName" size="20" name="fileName" />
<input type="submit" name="submit">
</form>
</body>
</html>
If you have the name attribute defined on the input control, the file name will be in the FORM scope. For example:
<cfif not structIsEmpty(form)>
<cfdump var="#form#">
<cfelse>
<html>
<head>
<title>Title</title>
</head>
<body>
<form method="POST" action="#cgi.SCRIPT_NAME#">
<input type="file" name="fileIn" />
<input type="Submit" name="formSubmit">
</form>
</body>
</html>
</cfif>
Another option might be to have client-side code populate a hidden form field with the filename, which you would then have server-side.
Ben Doom's answer is generally how I would approach it, though.
Here's how we do it. Basically, there is a file field, and a string field. JavaScript grabs the filename from the browser before the form is submitted. Obviously, you need to verify that the filename on the other end is actually present (it'll be blank if the user has JavaScript disabled, for example) and you'll need to parse the string to handle platform differences (/users/bob/file.jpg versus C:\Documents and Settings\bob\file.jpg)
<script>
function WriteClientFileName(){
$('ClientFileName').value = $('ClientFile').value;
}
</script>
<form enctype="multipart/form-data" onsubmit="WriteClientFileName();">
<input type="File" name="ClientFile" id="ClientFile">
<input type="hidden" name="ClientFileName" id="ClientFileName" value="">
<input type="submit">
</form>
Incidentally, this technique is cross-language. It'll work equally well in RoR, PHP, JSP, etc.
Edit: If a user is "wielding a fierce FireBug" what's the issue? Even if they don't have Firebug, they can still rename the file on their end and change the input. Plus, you're validating your inputs, right?
There is no way to know the file name for uploaded files before saving to the server in ColdFuson, Railo or OpenBD. I typically generate 'my' new filename using the createUUID() function in advance of saving the file.