Any idea why CFFILE Write does not support making it unique but CFFile Upload does?
I'm using CFFILE Write to handle a GetHttpRequestData, and being able to support making it unique would be very helpful. Any ideas?
Well, it doesn't. You'll have to roll your own unique names by first checking if the file exists and if it does, then add on some additional character(s) and repeat check/add until you've found something unique.
Alternatively, you always prepend something that ought to be fairly unique (e.g. date-time-incremental number).
try something like this
<cfscript>
i = 1;
myPath = 'D:\webroot\sap\returns\log';
myFileName = orderNumber;
</cfscript>
<cfloop condition="fileExists('#myPath#\#myFileName#.xml')">
<cfscript>
myFileName = '#orderNumber#_#i#';
i += 1;
</cfscript>
<cfif i GT 100><cfbreak /></cfif>
</cfloop>
<cffile action="write" file="#myPath#\#myFileName#.xml" output="#xmlString#" />
How about using CreateUUID() as filename?
Related
I need to use the FilegetMimeType() function, and I tested it in a program called demomimetype.cfm.
<cfoutput>
<cfset reploc = '#cookie.moxlog#'>
<cfset extrepoc = ExpandPath('#reploc#')>
<cfset typegif = FilegetMimeType('#extrepoc#/brightpink1.gif')>
typegif brightpink1.gif #typegif#<br>
</cfoutput>
This program returns the result that typegif is "image/gif" which is exactly correct. When I add the lines
<cfheader name = "Content-Disposition" value="attachment;#extrepoc#/logo106">
<cfcontent type="#typegif#" file="#extrepoc#/brightpink1.gif">
I get this result:
This is what I was hoping for, although demomimetype.cfm is not what I chose to open. However, this screen does open brightpink1.gif, so I'm okay with it.
Then I tried to integrate this into my actual system using this program, which results from a submit in an earlier program:
<cfoutput>
<cfset col = form.thecol>
<cfset colarray = getPageContext().getRequest().getParameterValues('xcol')>
<cfset origval = getPageContext().getRequest().getParameterValues('#col#')>
<cfset attname = origval[1]>
<cfset extreploc = ExpandPath("#cookie.moxlog#")>
<cfset typeatt = FilegetMimeType('#extreploc#/#attname#')>
</cfoutput>
The variable "attname" will be "brightpink1.gif. When I output typeatt I get "application/octet-stream", which is not correct. And when I add the lines
<cfheader name = "Content-Disposition" value="attachment;#extreploc#/#attname#">
<cfcontent type="#typeatt#" file="#extreploc#/#attname#">
I get the wrong result -- not surprising because the mimetype is wrong:
Obviously the demo is working correctly, and the real application is not. I have tried making the filename in the demo to be a variable, and the demo goes on working. I have tried making the filename in the application specific, and it still does not work.
I don't want to use the "strict" parameter because some of the files do not have extensions, even though they are of a specific type like .jpg.
There must be some reason why the code in the demo works and the nearly identical code in the application does not. But I cannot see it. Can anyone figure out what is going on here?
Is there a way to exclude certain items by filetype in a list in Coldfusion?
Background: I just integrated a compression tool into an existing application and ran into the problem of the person's prior code would automatically grab the file from the upload destination on the server and push it to the Network Attached Storage. The aim now is to stop their NAS migration code from moving all files to the NAS, only those which are not PDF's. What I want to do is loop through their variable that stores the names of the files uploaded, and exclude the pdf's from the list then pass the list onto the NAS code, so all non pdf's are moved and all pdf's uploaded remain on the server. Working with their code is a challenge as no one commented or documented anything and I've been trying several approaches.
<cffile action="upload" destination= "c:\uploads\" result="myfiles" nameconflict="makeunique" >
<cfset fileSys = CreateObject('component','cfc.FileManagement')>
<cfif Len(get.realec_transactionid)>
<cfset internalOnly=1 >
</cfif>
**This line below is what I want to loop through and exclude file names
with pdf extensions **
<cfset uploadedfilenames='#myfiles.clientFile#' >
<CFSET a_insert_time = #TimeFormat(Now(), "HH:mm:ss")#>
<CFSET a_insert_date = #DateFormat(Now(), "mm-dd-yyyy")#>
**This line calls their method from another cfc that has all the file
migration methods.**
<cfset new_file_name = #fileSys.MoveFromUploads(uploadedfilenames)#>
**Once it moves the file to the NAS, it inserts the file info into the
DB table here**
<cfquery name="addFile" datasource="#request.dsn#">
INSERT INTO upload_many (title_id, fileDate, filetime, fileupload)
VALUES('#get.title_id#', '#dateTimeStamp#', '#a_insert_time#', '#new_file_name#')
</cfquery>
<cfelse>
<cffile action="upload" destination= #ExpandPath("./uploaded_files/zip.txt")# nameconflict="overwrite" >
</cfif>
Update 6/18
Trying the recommended code helps with the issue of sorting out filetypes when tested outside of the application, but anytime its integrated into the application to operate on the variable uploadedfilenames the rest of the application fails and the multi-file upload module just throws a status 500 error and no errors are reported in the CF logs. I've found that simply trying to run a cfloop on another variable not related to anything in the code still causes it to error.
As per my understanding, you want to filter-out file names with a specific file type/extension (ex: pdf) from the main list uploadedfilenames. This is one of the easiest ways:
<cfset lFileNames = "C:\myfiles\proj\icon-img-12.png,C:\myfiles\proj\sample-file.ppt,C:\myfiles\proj\fin-doc1.docx,C:\myfiles\proj\fin-doc2.pdf,C:\myfiles\proj\invoice-temp.docx,C:\myfiles\proj\invoice-final.pdf" />
<cfset lResultList = "" />
<cfset fileExtToExclude = "pdf" />
<cfloop list="#lFileNames#" index="fileItem" delimiters=",">
<cfif ListLast(ListLast(fileItem,'\'),'.') NEQ fileExtToExclude>
<cfset lResultList = ListAppend(lResultList,"#fileItem#") />
</cfif>
</cfloop>
Using only List Function provided by ColdFusion this is easily done, you can test and try the code here. I would recommend you to wrap this code around a function for easy handling. Another way to do it would be to use some complex regular expression on the list (if you're looking for a more general solution, outside the context of ColdFusion).
Now, applying the solution to your problem:
<cfset uploadedfilenames='#myfiles.clientFile#' >
<cfset lResultList = "" />
<cfset fileExtToExclude = "pdf" />
<cfloop list="#uploadedfilenames#" index="fileItem" delimiters=",">
<cfif ListLast(ListLast(fileItem,'\'),'.') NEQ fileExtToExclude>
<cfset lResultList = ListAppend(lResultList,fileItem) />
</cfif>
</cfloop>
<cfset uploadedfilenames = lResultList />
<!--- rest of your code continues --->
The result list lResultList is copied to the original variable uploadedfilenames.
I hope I'm not misunderstanding the question, but why don't you just wrap all of that in an if-statement that reads the full file name? Whether the files are coming one by one or through a delimited list, it should be easy to work around.
<cfif !listContains(ListName, '.pdf')>
OR
<cfif FileName does not contain '.pdf'>
then
all the code you posted
I have a form where users are to upload at least three documents and up to seven. In its current state, I have all the error checking and validation functioning. What I want to happen is for the visitor's forms to get renamed a specified name once they are uploaded and placed into a specified directory. My code is here: http://pastebin.com/V5ThWe7M
I believe the issue occurs around line 456. I believe I need to have the file name stored in a variable then use the variable to process the renaming function. The first file gets uploaded but not the second as they are assigned the same names. I'm trying to figure out how to use the variables that store the individual file names and then use that variable to rename the file. I'd like to have another set of eyes check it out for me and point me in the right direction.
Thank you
As already mentioned, the CFFILE structure is overwritten each time you upload. So any values you wish to preserve, you must save to another variable. But since you are already saving the full file names to a variable, you could easily extract their extensions using list functions. For example:
<cfset nomExt = listLast(clientNominationLetter, ".")>
A few other observations
Consider a more unique naming scheme for your folders than "/firstName_lastName/". Otherwise, you may end up overwriting someone's files if you receive multiple submissions under the same name, like two different "John Smith's".
The cffile values are separated into two categories: cffile.serverXX and cffile.clientXX (ie user system). They are not interchangeable. So be sure you are using the correct variables and be consistent.
FILE is deprecated. Use CFFILE or the result attribute instead.
EDIT: Adding some new code into the mix... :)
Here's some functions, just slap these in the top of your page...
<cffunction name="uploadFile">
<cfargument name="formField" hint="Form field name that holds the file to be uploaded" required="yes">
<cfargument name="renameTo" hint="What to rename the file, ex: 01_nominationLetter" required="yes">
<cfargument name="uploadErrorMessage" required="no" default="Error uploading file"/>
<cfargument name="allowedExtensions" required="no" default="doc,docx,pdf,txt,rt">
<cfargument name="extensionErrorMessage" required="no" default="Only doc, docx, pdf, txt, and rtf file formats are accepted">
<cfset var dir = expandPath("./nominationUploads/#trim(form.fname)#_#trim(form.lname)#/")>
<cfparam name="request.filesUploaded" default="#arrayNew(1)#">
<cftry>
<cffile action="upload" filefield="#arguments.formField#" nameconflict="makeunique" destination="#dir#">
<cfcatch type="any"><cfset ArrayAppend(arrErrors, arguments.uploadErrorMessage )></cfcatch>
</cftry>
<cfif not listFindNoCase(arguments.allowedExtensions, cffile.ServerFileExt)>
<cfset ArrayAppend(arrErrors, arguments.extensionErrorMessage )>
</cfif>
<cffile action="rename" file="#dir##cffile.serverFile#" destination="#dir##renameTo#.#cffile.ServerFileExt#">
<cfset ArrayAppend(request.filesUploaded, dir & arguments.renameTo & "." & cffile.ServerFileExt )>
</cffunction>
<cffunction name="removeFilesOnError">
<cfloop from="1" to="#arrayLen(request.filesUploaded)#" index="i">
<cftry><cffile action="delete" file="#request.filesUploaded[i]#"/><cfcatch type="any"></cfcatch></cftry>
</cfloop>
</cffunction>
Then in your existing validation script, get rid of all the file stuff, instead you'll only need to use those functions above, something like this :
<cfset uploadFile('myFileField', 'renameToThis' )>
<cfset uploadFile('myFileField2', 'renameToThat' , 'My custom upload error!')>
<cfset uploadFile('anotherFile', 'differentName', 'Another custom upload msg!', 'doc,docx', 'This one only lets you upload word docs!')>
<cfif arrayLen( arrErrors ) >
<cfset removeFilesOnError()>
</cfif>
I don't have time to test the above, but I believe it to be frighteningly close. If you encounter an issue, let me know and I'll help you debug it out. :)
I'm using CFScript to create and save a spreadsheet. However, SpreadsheetWrite isn't producing a file.
<cfscript>
...
...
...
spreadsheetWrite(sObj, yourSheet, "yes");
</cfscript>
It does not produce an error, but there's no file either. If I remove spreadsheetwrite from the cfscript tag and use:
<cfspreadsheet action = "write" fileName = "#yourSheet#" name = "sObj">
... I get the file just fine.
Was this not the intended use of SpreadsheetWrite()? Also, is it possible to produce a spreadsheet directly to the browser like cfdocument, or do I have to save the file first?
(Update: With relative paths it may be saving the file somewhere you are not expecting. Use an absolute path so there are no guessing games.)
This works for me using absolute paths.
<cfscript>
yourSheet = "c:/myFile.xls";
sObj = SpreadSheetNew("MySheet");
SpreadsheetAddRow(sObj,"Foo,Bar",1,1);
SpreadSheetWrite(sObj, yourSheet, true);
WriteOutput("Exists?= "& FileExists(yourSheet));
</cfscript>
To display the spreadsheet in the browser, use SpreadSheetReadBinary and cfcontent. Obviously use the appropriate mime type and file extension.
<cfheader name="Content-Disposition" value="attachment; filename=SomeFile.xls" />
<cfcontent type="application/vnd.msexcel" variable="#SpreadSheetReadBinary(yourSpreadSheetObject)#" />
Let's say i've just parsed someone else's XML document which is a response to an API request. I want to know if a value nested deep inside exists. If my API request worked, it will be in the same place every time. If my API request fails, the root of the XML is very different.
If I try <cfif structKeyExists(myStruct.level1.level2.level3, 'myTarget')> on a failed api request, I get the fatal error: Element LEVEL1.LEVEL2 is undefined in MYSTRUCT.
Of course, I could try to depend on the root level of the XML telling me of success or failure, and not looking for the result if it failed, but... barring that solution, what should i do?
Do i need to check for the existence of each level of the struct? As in:
<cfif structKeyExists(myStruct, 'level1')
and structKeyExists(myStruct.level1, 'level2')
and structKeyExists(myStruct.level1.level2, 'level3')
and structKeyExists(myStruct.level1.level2.level3, 'myTarget')>
<!--- ... --->
</cfif>
This is not a real-world problem, this is just something i've faced too many times. Please don't tell me solutions that involve changing the API or solutions like those in the third paragraph.
Thanks!
edit: i should have mentioned why i can't use isDefined() - some of the keys do not have syntactically valid names, so isDefined() throws an error, eg myStruct.level1[42].level3
XMLSearch
I would use the parsed XML document (i.e. xmlDoc) and XMLSearch:
<cfset xmlDoc = xmlParse(responseData)>
<cfset nodes = XmlSearch(xmlDoc, '/level1/level2/level3/myTarget')>
<cfif arrayLen(nodes)>
<!--- do something, you have the "nodes" array to work with too --->
</cfif>
xpath for XMLSearch() assumes the structure keys are nodes. You would need to modify accordingly if, for instance, 'myTarget' is an attribute of a node.
StructFindKey
Another way of doing this would be StructFindKey.
<cfset result = structFindKey(myStruct, "myTarget")>
<cfif arrayLen(result) AND result.path EQ "level1.level2.level3">
<!--- do something --->
</cfif>
Conclusion
Haven't tested, but I believe either will be faster than using IsDefined() or a try-catch block. Has the advantage over XMLValidate() of not needing a DTD. And, even with a DTD, the node you want may be defined as optional, so it could still validate.
You could validate the XML against a DTD to make sure the document was in the right format. XmlParse() and XmlValidate() both take a DTD as a parameter.
<cfset validateResult = XmlValidate(myXmlDocument, myDTD)>
<cfif validateResult.status>
<!--- xml is valid continue processing --->
<cfelse>
<!--- xml did not validate handle the error --->
</cfif>
Personally I wouldn't go crazy checking for every level of a 'deep' structure like this. I would presume that if the top level exists the rest of the document will be as you expect, and I'd just address the document from there.
If you wanted you could perhaps try to address the value in your struct and wrap it in a try/catch. That way you can handle any errors at any 'level' in the same way.
<cftry>
<cfset myVar = myStruct.level1.level2.level3 />
<cfcatch type="any">
<!--- Handle error --->
</cfcatch>
</cftry>
Hope that helps some.
I know I'm going to get booed off the stage here, but this is where isDefined() can save you a lot of typing:
<cfif isDefined(structKeyExists(myStruct.level1.level2.level3)>
<!--- do something --->
</cfif>
I know this is a year old, but I'm going to put in an answer here. I struggled for a good long time with this one, till I found a simple solution. If I know the structure of the XML already, a simple IsDefined works to test if the node or node attribute exists. I don't think most people know you can do this, or have tried and failed because they didn't include single quotes in the IsDefined function.
So say I grab some user xml from a web service somewhere and want to display the user's ID.
<cfhttp url="https://mycompany.com/mywebservices/getusers" username="me" password="mysecret">
<cfset userXMLDoc = XMLParse(ToString(cfhttp.FileContent).trim())>
<cfif IsDefined('userXMLDoc.Data.Record.User.XmlAttributes.id')>
<cfdump var="#userXMLDoc.Data.Record.User.XmlAttributes.id#">
<cfelse>
<cfoutput>Failed: No User ID found</cfoutput>
</cfif>