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.
Related
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
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.
can anyone review this code tell me what's wrong in it? I don't understand why it is displaying questions two times.
here is the code to display questions based on its questiontype, I mean it will look into question folder for matching questiontype template and then display it with question.
this is the code to show survey's questions.
<cfoutput>
<cfset step = 0 />
<form class="form form-horizontal" action="#buildUrl(action='survey.savesurveyresults',querystring='surveyId=#rc.surveyid#')#" method="post">
<input type="hidden" name="id" value="0">
<input type="hidden" name="fksurveyid" value="#rc.surveyId#">
<input type="hidden" name="fkquestionid" value="#rc.questions.id#">
<fieldset>
<cfloop query="rc.questions">
<cfset step ++ />
<cfset answer = "" />
<cfmodule template="../question/#rc.questions.template#/display.cfm" step="#step#" question="#rc.questions.question#" template1="#rc.questions.template#" fkquestionid="#rc.questions.id#" answer="#answer#" required="#rc.questions.required#" result="result#step#"/>
</cfloop>
<div class="form-actions">
<button type="submit" name="submit" class="btn btn-success">Submit answers</button>
</div>
</fieldset>
</form>
</cfoutput>
this is my display.cfm to view question and its questiontype like truefalse or yes or no.
<cfparam name="attributes.yesno" default="false">
<cfoutput>
<p>#attributes.step#) #attributes.question# <cfif attributes.required EQ 1><strong>* </strong></cfif></p>
<div class="answers">
<cfif attributes.yesno>
<input type="radio" name="answer" id="answer" value="yes"<cfif attributes.answer is "yes">Checked</cfif>><label for="truefalse">Yes</label><br>
<input type="radio" name="answer" id="answer" value="no"<cfif attributes.answer is "No">Checked</cfif>><label for="truefalse">No</label>
<cfelse>
<input type="radio" name="answer" id="answer" value="true"<cfif attributes.answer is "true">Checked</cfif>><label for="truefalse">True</label><br>
<input type="radio" name="answer" id="answer" value="False"<cfif attributes.answer is "False">Checked</cfif>><label for="truefalse">False</label>
</cfif>
</div>
</cfoutput>
here is the query to list question's records.
<cfquery name="list">
SELECT
questions.id,
questions.question,
questions.rank,
questions.required,
questiontypes.name as questiontype,
questiontypes.template as template,
surveys.name as surveysname,
surveys.thankyoumsg as thankyoumsg
FROM questions
INNER JOIN questiontypes ON questions.fkquestiontypeid = questiontypes.id
INNER JOIN surveys ON questions.fksurveyid = surveys.id
WHERE questions.fksurveyid = <cfqueryparam cfsqltype="cf_sql_bigint" value="#arguments.surveyid#">
</cfquery>
This is something that has bitten me a couple of times. I've always been pretty big on closing tags. But this is a situation where it will hurt. And be hard to debug if you don't understand the behavior of cfmodule. As the post above mine states, if you close the cfmodule tag, it will execute twice. This is because it's treated the same as a custom tag. There may be situations where you want to process part of the tag when it's first run and the rest after it's complete. You can access the ExecutionMode in the thisTag scope of the cfmodule page. With no closing tag, it's simply run in the thisTag.ExecutionMode = Start mode. If you close it, it runs the tag again in the End mode. If you aren't doing anything with the ExecutionMode inside the cfmodule's code, the whole thing will simply run again. This behavior is part of the reason that cfmodule can be so powerful.
When using the <cfmodule> tag you need to remember that ColdFusion will call that tag twice if you include an ending </cfmodule> tag OR if you close the opening tag like so <cfmodule ... />.
As stated on the cfmodule documentation page:
If you specify an end tag to cfmodule, ColdFusion calls your custom tag as if it had both a start and an end tag. For more information, see Handling end tags in the Developing ColdFusion Applications.
Handling end tags in the Developing ColdFusion Applications
In order to avoid this functionality do not close your <cfmodule> tag.
I got it, i must have not to close the cfmodule tag like <cfmodule />.
I have a form in ColdFusion that initially has 5 input fields for file uploading. Should the user realize they have more than 5 files to upload in the process of attaching them, I would like the form to preserve the values when it submits itself for the change in # of fields.
Using the <cfform> tag with the preservedata="yes" attribute is supposed to accomplish this - but all I'm getting is a temp value stored in the input's value on resubmit that isn't displayed in the field nor works for a submission.
edit: Thanks for the great answers everyone, you all helped and were correct. I was able to implement Adam's suggested solution. Works great! Thanks!
function changeFieldCount() // javascript function for submit on input count change
{
var count = document.forms[0].numtotal.options[document.forms[0].numtotal.selectedIndex].value;
document.forms[0].action = "me.cfm?count=" + count;
document.forms[0].submit();
}
<cfparam name="url.count" default="5">
<cfform name="dispfiles" method="post" enctype="multipart/form-data" preservedata="yes">
<!--- looping for file input fields --->
<cfloop index="counter" from="1" to="#url.count#">
<cfinput type="file" name="file#counter#" size="50" maxlength="60"><br>
</cfloop>
<br>Number of Files to Attach:
<!--- looping for # of input selection--->
<cfselect name="numtotal">
<cfloop index="cnt" from="1" to="20" step="1">
<cfoutput>
<option value="#cnt#" <cfif #cnt# eq #url.count#>selected</cfif>>
#cnt#
</option>
</cfoutput>
</cfloop>
</cfselect>
<cfinput type="button" name="op-display" value="Display" onclick="changeFieldCount()">
<cfinput type="button" name="op-upload" value="Attach Files" onclick="submitfrm(this.form)">
<cfinput type="button" name="cancel" value=" Cancel " onclick="window.close()">
</cfform>
This is what I'm getting when I view source on the resulting submission:
<input name="file1" id="file1" type="file" value=".../cfusion.war/neotmp8858718543274653167.tmp" maxlength="60" size="50" /><br>
This is by design in all browsers for security reasons. There is no way you can insert the value for a file field.
To elaborate on #SpliFF's answer, what you need to do is dynamically create more file fields with JavaScript.
Here's an example that uses jQuery to make the JavaScript a little easier. I've also used a variable to track the number of file fields displayed so that they all have a unique number appended to their names, making it possible to loop over and uniquely identify them server-side.
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
//track the current number of file fields displayed
var numUploadFields = 5;
//attach an anonymous function to the button to add more fields
$(function(){
$("#add5").click(function(){
for (var i = 0; i < 5; i++){
numUploadFields += 1;
var newHTML = "<br/><input type='file' name='upload" +
numUploadFields + "' />";
$("#someSection").append(newHTML);
}
});
});
</script>
<form method="post" action="">
<div id="someSection">
<input type="file" name="upload1" /><br/>
<input type="file" name="upload2" /><br/>
<input type="file" name="upload3" /><br/>
<input type="file" name="upload4" /><br/>
<input type="file" name="upload5" />
</div>
<input type="button" id="add5" value="Add 5 more file fields" />
</form>
Build additional file inputs using JS DOM methods. Since you aren't leaving the page this is fast and nothing is lost.