CFFILE - Uploading a file using a component - coldfusion

I have a form that I would like to submit to a component for processing (CRUD behaviors), the problem is it seems passing multipart/form-data to a component somehow looses the file location. When it gets to the part of the component that should be uploading the file I get the infamous form field did not contain a file error.
I am not 100% sure why this happening but if I submit the form directly to a .cfm page that performs the cffile action everything works as expected, but if the .cfm page does something like:
<cfobject name="process_form" component="processor" />
<cfset result = process_form.upload( form ) />
and the component "processor" tries to do the upload, I get the form field did not contain a file.
My processor looks like:
<cfcomponent name="processor">
<cffunction name="upload" returntype="string">
<cfargument name="form_data" type="struct" />
<cffile action="upload" filefield="#arguments.form_data.file_1#" ...>
[ ... ]
</cffunction>
</cfcomponent>
One thing to note, is if I try use the variable arguments.form_data.file_1 without the # signs around it, I get the error:
The form field arguments.form_data.file_1 did not contain a file.
If I put the # signs around the variable I get:
The form field C:\JRun4\servers\cfusion\SERVER-INF\temp\cfusion-war-tmp\neotmp7350969777287007477.tmp did not contain a file.
Any idea on how to fix this issue? I would rather have all my processing actions inside a component, but right now I can't seem to make that work.
Thanks!
Will

You shouldn't need to use the full variable name when using a cffile tag--you just need the form field name, so something like:
<cffile action="upload" filefield="file_1" ...>
should suffice. The FORM struct field holds the location of the temporary file, but the cffile tag doesn't need that (I'd image that id directly accesses the FORM struct on the backend based on the fieldname you've provided).

Related

Enctype issue when I try to upload a file with a Coldfusion webservice

Can't get to work a Coldfusion webservice that uploads a file in an external server.
My application runs in "Server A" and the file repository (external server) is in "Server B"
The template (inicio.cfm) which contains the form with the <cfinput type="file"> to select the client's file to be uploaded, is stored in "Server A". This template performs more tasks than just show the upload form. It performs calculations, queries, etc. outside the form's code block. Also, the action page of this form is the template itself (because of my application's needed tasks).
The first code line of my form definition is (inside inicio.cfm):
<cfform method="post" name="AltaConvocatoria" enctype="multipart/form-data">
Which demonstrate that I'm using the right enctype definition.
In the other hand, stored in "Server B" is my Coldfusion component or webservice (alta_ga.cfc) which only task is to upload the file selected by user in "inicio.cfm" form and rename it.
Here's alta_ga.cfc code:
<cfcomponent>
<cffunction access="remote" returntype="void" name="cargaAnuncio">
<cfargument name="destinoAnuncio" required="yes" type="string">
<cfargument name="PrefijoNvoNombre" required="yes" type="string">
<cffile action="upload"
fileField="str_ArchivoAnuncio"
destination="#destinoAnuncio#"
nameconflict="Overwrite">
<cfset NvoNomAnuncio = #PrefijoNvoNombre# & #Right(cffile.ClientFile, 5)#>
<cfset viejoNombre1 = #destinoAnuncio# & #cffile.ClientFile#>
<cffile
action = "rename"
destination = "#NvoNomAnuncio#"
source = "#viejoNombre1#">
</cffunction>
</cfcomponent>
For that pupose, I invoke the webservice from the form's action code block in inicio.cfm with this:
<cfinvoke webservice="http://192.168.208.128/podi/mgmt/alta_ga.cfc?wsdl" method="cargaAnuncio" >
<cfinvokeargument name="destinoAnuncio" value="#form.destinoAnuncio#" />
<cfinvokeargument name="PrefijoNvoNombre" value="#form.PrefijoNvoNombre#" />
</cfinvoke>
When I try to load a file using my form's template inicio.cfm I get this message:
Cannot perform web service invocation cargaAnuncio.
The fault returned when invoking the web service operation is:
'' podi.mgmt.PodiMgmtAlta_gaCfcCFCInvocationExceptionException: coldfusion.tagext.io.FileUtils$CFFileNonMultipartException : Invalid content type: application/soap+xml; charset=UTF-8; action="urn:cargaAnuncio".The files upload action requires forms to use enctype="multipart/form-data".]
All the arguments and variables that I'm using are correct, because I tested the webservice as a local component (stored in Server A and uploading the file in the same server) and worked fine. Here's the code of the succesful test (invoked as a component instead of a webservice):
<cfinvoke component="alta_ga" method="cargaAnuncio" destinoAnuncio="#form.destinoAnuncio#" PrefijoNvoNombre="#form.PrefijoNvoNombre#">
¿What could be wrong?
There's a lack of documentation about this functionality. Adobe's user guide doesn't explain this functionality in depht. Ben Forta's books... same. Or I couldn't find the information.
Thanks in advance.
When a form is posted to a CFML server, the posted file is saved in a temporary directory before any of your code runs. All <cffile action="upload"> does is to copy a file from that temporary directory to the location you want it to be. Your remote server ServerB has no idea about any file posted on ServerA, so <cffile action="upload"> will not help you.
The action is misleading. It's not upload-ing anything. It's just copying from a predetermined temp directory. The web server handles the uploading before the CF server is even involved.
You will likely need to <cffile action="upload"> on ServerA to a specific place, and then it needs to post that file to your web service on ServerB. Then ServerB should be able to use <cffile action="upload"> to transfer it from the upload temp directory to wherever you need it to be. That said I have never tried this when posting to a web service.
Alternatively you could just post the file directly to ServerB in the first place, to save needing ServerA to be an intermediary. This might not be possible, of course.

coldfusion cffile upload how to describe the file to be uploaded

I've never used cffile upload. In looking at the documentation I see that the file to be uploaded is described as
Name of form field used to select the file.
Do not use number signs (#) to specify the field name.
I just cannot decipher this. If the file to be uploaded is john.jpg, residing on the user's disk, how do I indicate that in the cffile command?
I have other questions as well, but would like to start with this very basic one.
What documentation are you using? There should be an example like there is here: <cffile action="upload">
In this example (which I've edited), it shows that you do not reference the name of the file that the user selected, that could be anything, you reference the name of the form field, fileContents, that is used to upload the file.
<!--- Windows Example --->
<!--- Check to see if the Form variable exists. --->
<cfif structKeyExists(Form, "FileContents") >
<!--- If TRUE, upload the file. --->
<cffile action = "upload"
fileField = "FileContents"
destination = "c:\files\upload\"
accept = "text/html"
nameConflict = "MakeUnique">
<cfelse>
<!--- If FALSE, show the Form. --->
<form method="post" action=<cfoutput>#cgi.script_name#</cfoutput>
name="uploadForm" enctype="multipart/form-data">
<input name="FileContents" type="file">
<input name="submit" type="submit" value="Upload File">
</form>
</cfif>
Once the CFFILE process completes, a collection of variables are defined in a structure named CFFILE (see documentation link). One of these variables is cffile.clientFile, which contains the name of the file that was uploaded from the user's computer.
For the cffile command:
<cffile action = "upload"
fileField = "FileContents"
destination = "c:\files\upload\"
accept = "text/html"
nameConflict = "MakeUnique"
result = "thisResult">
...you would use #thisResult.clientFile# to get the original filename.
All the other fields would be available using this also.
I found this on stackoverflow:
ColdFusion ServerFile is undefined in CFFile

Duplicating and Renaming a file and storing name in Database

I have a register page that asks for information like employee_number, user_name, user_pass, firstname, lastname, position, email, phone_extension, department, picture. Once they fill this information out and they hit register it all gets uploaded into a database.
Is it possible to have it create a profile page specific to that user based on the information in the database?
For example, if I registered to have it create DavidBriertonProfile.cfm from my template Profile.cfm and have it add DavidBriertonProfile.cfm in the database so I can use that name to reference later. But is it possible to take my template Profile.cfm and rename it based on there name and have it added to profiles/(TherenameProfile).cfm
I have been playing with cffile in order to create a path but I need it to be behind the scenes selecting my template file where the user never sees any of this.
<cffile
action = "upload"
file = "#expandPath("/webapps/dash/profiles/profile.cfm")#"
destination = "#expandPath("/webapps/dash/profiles/")#"
nameConflict = "MakeUnique"
result = "myfile"
/>
There are two primary options
Create a static file from a coldfusion template...
<cfsavecontent variable="filecontent">
<cfinclude template="profile.cfm" />
</cfsavecontent>
<cffile action="write" file="profiles/#FirstNameLastName#Profile.html" output="#filecontent#" />
<!--- it looks like the "nameconflict" option is only available for upload action so will have to deal with that --->
Create a file just for setting the userid and including profile.cfm
<cffile action="write"
file="profiles/#FirstNameLastName#.cfm"
output="<cfset userid ='#UserID' /><cfinclude template='../profile.cfm' />"
/>
Some other options include
Save the name of the unique cfm file you would create (ex: DavidSmith12Profile) but don't actually create it and instead use the OnMissingTemplate function in Application.cfc to take the name supplied and perform a database lookup and then show the profile result
Peform a URL rewrite on the webserver to transform any request to paths of format /profiles/(.+) to /profile.cfm?filename={\1} and then do a database lookup by the filename directly in profile.cfm
enjoy

How to change the user image using cfldap?

I'm able to get all the values that I want from cfldap.
But when I try to update the user image I don't know how to send the correct value for the binary image attribute.
I tried getting the image variable from the cffile upload
<cffile action="UPLOAD" filefield="file" destination="c:\inetpub\wwwroot\test" nameconflict="OVERWRITE" result="image" />
Also tried using cfimage with a static image -
<cfimage action="read" source="c:\inetpub\wwwroot\test\image.png" name="anotherImage">
Or even with
<cffile action="READBINARY" file="c:\inetpub\wwwroot\test\image.png" variable="BinaryImageContent">
But in any case, when I call
<cfldap action="modify"
DN="#results.dn#"
attributes="thumbnailPhoto=#obj.image#"
modifytype="replace"
server="myserver"
username="mydomain\myuser"
password="mypass">
The #results.dn# is the DN from the user that I get before (Everything ok on that)
I created the #obj.image# to be able to try all types of variables
Also tried these params:
<cfset obj.test1 = BinaryImageContent />
<cfdump var="#imageGetBlob(anotherImage)#" />
<cfdump var="#toString(obj.test1)#" />
By the way, the error that I get its
One or more of the required attributes may be missing or incorrect or
you do not have permissions to execute this operation on the server.
The problem is that I'm using the domain administrator account to update that
(THIS ERROR IS SOLVED - The network guys hadn't given me this permission... now I have it).
Now what I'm using is the following:
<cffile action="UPLOAD" filefield="file" destination="c:\inetpub\wwwroot\test" nameconflict="OVERWRITE" result="imagem" />
<cfset filename = "C:\inetpub\wwwroot\test\#imagem.serverFile#">
<cffile action="readbinary" file="#filename#" variable="img">
<cfset imgStr = BinaryEncode(img, "hex")>
<cfset imgStr2 = REReplace(imgStr, "..", "\\\0", "ALL")>
<cfldap
action="modify"
DN="#results.dn#"
attributes="thumbnailPhoto=#imgStr2#"
modifytype="replace"
server="myserver"
username="mydomain\myuser"
password="mypass"
>
but I get this binary code
Whats strange, is that before I had a binary code like -1-41 and now, nothing similar...
and when I try to show the pic
And this is one correct image....
EDIT: The original code sample below shows how it could work if ColdFusion wouldn't have a bug (or "very unfortunate design decision") in CFLDAP.
CFLDAP encodes the parameter values you pass to it before sending them to the server. This is nice because you don't have to worry about value encoding. But... it is also not helpful because it means you can't send encoded values yourself anymore, since CF invariably encodes them again.
Bottom line: As far as LDAP is concerned, encoding a file into a hex-string is correct, but CFLDAP mangles that string before sending it to the server. Combined with the fact that CFLDAP does not accept raw binary data this means that you can't use it to update binary attributes.
The comments contain a suggestion for a 3rd-party command line tool that can easily substitute CFLDAP for this task.
You need to send an encoded string to the server as the attribute value. The encoding scheme for binary data in LDAP queries has the form of attribute=\01\02\03\ab\af\cd.
Read your image into a byte array, encode that array into a hex string and prefix every encoded byte with a backslash.
<cffile action="readbinary" file="#filename#" variable="img">
<cfset imgStr = BinaryEncode(img, "hex")>
<cfset imgStr = REReplace(imgStr, "..", "\\\0", "ALL")>
<cfldap
action="modify"
DN="#results.dn#"
attributes="thumbnailPhoto=#imgStr#"
modifytype="replace"
server="myserver"
username="mydomain\myuser"
password="mypass"
>
Also don't forget what the documentation has to say about modifyType.

ColdFusion CFC returning record count

Question: I have a CFM calling my CFC on the cfform action line:
In my CFC, I have output="false". I am needing the record count sent back to my CFM. When I run my CFM and enter the form info my queries are running successfully, but it is not coming back to my CFM so I can display the proper messages. I just get the CFC page with my record count. Any suggestions? Thanks!
Since there's no code, making a few assumptions here about how you're doing things ....
Don't point to the CFC, point to a CFM page in your <cfform>. (If you omit the action, it'll point back to itself.. I like self-referencing form pages)
In your CFC, return the result struct from your query:
<cfquery datasource="#ds#" name="myQuery" result="myResult">
INSERT INTO myTable .....
</cfquery>
Then either return that entire struct, or just myResult.recordCount:
<cfreturn myResult.recordCount>
Then in your CFM page, you'll access it like so (assuming you're using <cfscript>; similar if you're doing tag-based):
recordsAdded = createObject('component','myFolder.myCFC').insertMethod(form);
I changed the cfform line to this:
....rest of my form
then added these lines to see if the form was submitted and what action is:
<!--- create object for cfc --->
But now, my query is not running....