Downloading large file in ColdFusion using CFHTTP - coldfusion

I'm trying to download a large (600MB) binary file to the server using CFHTTP on Coldfusion 8:
<cfhttp
method="get"
url="#fileURL#"
path="#filePath#"
file="#fileName#"
timeout="600"
getasbinary="auto"
>
It's working fine for smaller files (100something MB) but for the large one's I'm getting the Server 500 error: "java.lang.OutOfMemoryError: Java heap space".
The file is being downloaded from a Dropbox folder - so only available option is to use HTTP GET.
Does anyone have idea how to download it, so it wouldn't kill the server or timeout?

You can do this by calling Java from CF code. The buffered input and output stream classees are designed to hold onto chunks of data, rather than the whole thing, avoiding OutOfMemory errors.
getByteArray() is a helper method because there's not way to declare something like byte buf[]=new byte[1024]; in CF directly.
In the example change the source and destination variables.
Example
<cfset source = 'protocol://domain/path/to/file.ext'>
<cfset destination = getDirectoryFromPath(getCurrentTemplatePath()) & listlast(source, "/")>
<cffunction name="getByteArray" access="private" returnType="binary" output="no">
<cfargument name="size" type="numeric" required="true"/>
<cfset var emptyByteArray =
createObject("java", "java.io.ByteArrayOutputStream").init().toByteArray()/>
<cfset var byteClass = emptyByteArray.getClass().getComponentType()/>
<cfset var byteArray =
createObject("java","java.lang.reflect.Array").newInstance(byteClass, arguments.size)/>
<cfreturn byteArray/>
</cffunction>
<cfscript>
uri = createObject("java", "java.net.URL").init(source);
uis = uri.openStream();
bis = createObject("java", "java.io.BufferedInputStream").init(uis);
fos = createObject("java", "java.io.FileOutputStream").init(destination);
bos = createObject("java", "java.io.BufferedOutputStream").init(fos);
buffer = getByteArray(1024);
len = bis.read(buffer);
while(len > 0) {
bos.write(buffer,0,len);
len = bis.read(buffer);
}
bos.close();
bis.close();
fos.close();
uis.close();
</cfscript>

The problem is is that it's too large. ColdFusion reads the entire thing into memory before writing it to disk.
You'll be better off using some other method to get the file. wget can do http requests from a command-line. That with judicious use of CFEXECUTE is probably a good way to go.

Related

queryEach or .each() not Working at CF 11, Why?

I was seaching in google better form to interate over "query" in coldfusion, since im new in the company im working, and im trying to get more from CF
Here my attempts:
My models:
<cffunction hint="Foo" name="Foo" access="public" returntype="query">
<!--- Argumentos --->
<cfargument hint="Something" name="ArgComBus" type="string" required="no" default="">
<cfargument hint="Other thing" name="ArgPar" type="string" required="no" default="">
<cfscript>
queryService = new Query();
queryService.setSql("
SELECT
column1,
column2,
FROM
tab_bar
WHERE
1=1
#arguments.ArgComBus#
");
queryService.setDataSource(session.Dsn);
if(Len(Trim(arguments.ArgPar))){
Evaluate(arguments.ArgPar);
}
queryResult = queryService.execute();
qBus = queryResult.getResult();
</cfscript>
<cfreturn qBus>
</cffunction>
My script
<cfscript>
arrFoo = arrayNew(1);
qFoo = this.Foo(
ArgComBus = " AND column1 = #variables.bar# ");
// First Attempt - The each method was not found.
qFoo.each(function (foo) {
arrFoo.append(foo);
});
// Second Attempt - Variable QUERYEACH is undefined.
queryEach(qFoo, function (foo) {
arrFoo.append(foo);
});
writeDump(arrFoo);
</cfscript>
My Server Dump
InstallKit Native Windows
appserver Tomcat
productlevel Developer
productname ColdFusion Server
productversion 11,0,05,293506
rootdir C:\CFusion11\cfusion
I even used getMetaData() on my query variable qFoo and that return that is array... so when i tried use something like that (trying to convert array in query?)
cfQuery = createObject("java", "coldfusion.sql.QueryTable").init(qFoo);
.each() and queryEach() same answer... i even tried use arrayEach() but return the object is coldfusion.sql.QueryTable and not array
You are running ColdFusion 11.
The queryEach() function was not added until ColdFusion 2016:
reference 1 (from cfdocs)
reference 2 (from Adobe docs)
Originally I had posted that the each() member function was not available in Adobe ColdFusion 11. Aquitaine pointed out in the comments that it actually is. I incorrectly referenced the Each() function for Lucee that works with collections. The Each() function related to this question is actually the script version of the ArrayEach() tag function. Which is available in ColdFusion 11 (it was actually added in ColdFusion 10). Sorry for the confusion.
The documentation may be wrong. I could not get the function to work as Each() except under ColdFusion 2018. For ColdFusion 11 I could only get it to work as ArrayEach().
reference 3 (from cfdocs)
reference 4 (from Adobe docs)
Here are a couple of examples on how to loop over a query in ColdFusion 11 (borrowed from cfdocs):
// Define our query
platform = ["Adobe ColdFusion", "Railo", "Lucee"];
myQuery = queryNew(" ");
queryAddColumn(myQuery, "platform", "CF_SQL_VARCHAR", platform);
// By row index
for (i = 1; i <= myQuery.recordCount; i++) {
writeOutput("<li>#myQuery["platform"][i]#</li>");
}
// By query
for (row in myQuery) {
writeOutput("<li>#row.platform#</li>");
}
// By arrayeach
writeOutput("<h3>By arrayeach:</h3>");
function printArray(vendor, index)
{
writeOutput("<li>#vendor#</li>");
}
arrayEach(platform,printArray);
I created a gist for you on TryCF.com so you can see this code in action and play around with it if you like. Just click here to run the code.

Base64 decoding error in coldfusion

I'm trying to decode a base64 string with #ToString( ToBinary( stringToDecode ) )# and coldfusion gives an error saying the parameter must be a base-64 encoded string. The string comes from a third party, and is supposed to be a pdf file. Here is a part of it:
JVBERi0xLjUKJeLjz9MKMSAwIG9iago8PC9UeXBlL1hPYmplY3QvUmVzb3VyY2VzPDw+Pi9TdWJ0 eXBlL0Zvcm0vQkJveFswIDAgMTUuNDQgMTUuNDZdL01hdHJpeCBbMSAwIDAgMSAwIDBdL0xlbmd0 aCAyMi9Gb3JtVHlwZSAxL0ZpbHRlci9GbGF0ZURlY29kZT4+c3RyZWFtCk9lHxbaEDXZpIO1A/Gj mGuxQdW3qkgKZW5kc3RyZWFtCmVuZG9iagoyIDAgb2JqCjw8L1R5cGUvWE9iamVjdC9SZXNvdXJj ZXM8PD4+L1N1YnR5cGUvRm9ybS9CQm94WzAgMCAxNS40NCAxNS4xOV0vTWF0cml4IFsxIDAgMCAx IDAgMF0vTGVuZ3RoIDIyL0Zvcm1UeXBlIDEvRmlsdGVyL0ZsYXRlRGVjb2RlPj5zdHJlYW0KtDN8 NkUKYx04Gj8V9LfOboGvN9VRIgplbmRzdHJlYW0KZW5kb2JqCjMgMCBvYmoKPDwvVHlwZS9YT2Jq ZWN0L1Jlc291cmNlczw8Pj4vU3VidHlwZS9Gb3JtL0JCb3hbMCAwIDE1LjQ0IDE1LjcyXS9NYXRy aXggWzEgMCAwIDEgMCAwXS9MZW5ndGggMjIvRm9ybVR5cGUgMS9GaWx0ZXIvRmxhdGVEZWNvZGU+
What I had thought was the spaces were a problem, but I did a replace to replace all " " with "" and they are still in there. Is there something else I should be doing?
In my testing I can take a local pdf and convert it to a string to mimic the string you are getting from the API. I can then take it and convert it back to a PDF.
When I use the string you put above I cannot get it to work. Not sure if something is list from you pasting it here or not.
Here is my code, maybe you can alter it to use the string right from the api.
<cffile action="readbinary" file="#expandPath('./test.pdf')#" variable="thefile"/>
<cfset the_string = toString(theFile)/>
<cfscript>
patrick_string = ToBase64("JVBERi0xLjUKJeLjz9MKMSAwIG9iago8PC9UeXBlL1hPYmplY3QvUmVzb3VyY2VzPDw+Pi9TdWJ0 eXBlL0Zvcm0vQkJveFswIDAgMTUuNDQgMTUuNDZdL01hdHJpeCBbMSAwIDAgMSAwIDBdL0xlbmd0 aCAyMi9Gb3JtVHlwZSAxL0ZpbHRlci9GbGF0ZURlY29kZT4+c3RyZWFtCk9lHxbaEDXZpIO1A/Gj mGuxQdW3qkgKZW5kc3RyZWFtCmVuZG9iagoyIDAgb2JqCjw8L1R5cGUvWE9iamVjdC9SZXNvdXJj ZXM8PD4+L1N1YnR5cGUvRm9ybS9CQm94WzAgMCAxNS40NCAxNS4xOV0vTWF0cml4IFsxIDAgMCAx IDAgMF0vTGVuZ3RoIDIyL0Zvcm1UeXBlIDEvRmlsdGVyL0ZsYXRlRGVjb2RlPj5zdHJlYW0KtDN8 NkUKYx04Gj8V9LfOboGvN9VRIgplbmRzdHJlYW0KZW5kb2JqCjMgMCBvYmoKPDwvVHlwZS9YT2Jq ZWN0L1Jlc291cmNlczw8Pj4vU3VidHlwZS9Gb3JtL0JCb3hbMCAwIDE1LjQ0IDE1LjcyXS9NYXRy aXggWzEgMCAwIDEgMCAwXS9MZW5ndGggMjIvRm9ybVR5cGUgMS9GaWx0ZXIvRmxhdGVEZWNvZGU+");
string2 = toBinary(ToBase64(the_string));
binencode=BinaryEncode(string2, "Base64");
</cfscript>
<cffile action="write" file="#expandPath('./randy.pdf')#" output="#thefile#" addnewline="No" />
click
Updated Code:
<cffile action="readbinary" file="#expandPath('./test.pdf')#" variable="thefile"/>
<cfset the_string = toBase64(theFile)/>
<cfscript>
string2 = toBinary(the_string);
</cfscript>
<cffile action="write" file="#expandPath('./randy.pdf')#" output="#string2#" addnewline="No" />
click
Here's an updated cfscript way of doing it in case anyone is interested:
<cfscript>
// read the binary file and save it as a variable
myPdf = fileReadBinary( expandPath( "/test.pdf" ) );
// encode as base64
myPdf = toBase64( myPdf );
// output the base64 version to the browser
writedump( myPdf );
// convert back to binary
myPdf = toBinary( myPdf );
// write to a new PDF file
fileWrite( expandPath( "/new.pdf" ), myPdf );
</cfscript>

Some Functions seems to be not exist when creating new bucket or checking if bucket exists using OpenBD

I am using OpenBD and trying to check whether bucket exists or not on my S3 server, if it is not exist then, to create new bucket. Here's my code:
index.cfm
<cfset request.awsaccess = "zzzzawsaccesszzzz">
<cfset request.awskey = "zzzzzzzzawskeyzzzzzzzz">
<cfset request.datasource="tcs">
<cfset request.region="us-west-2">
<cfscript>
AmazonRegisterdatasource(datasource=request.datasource,awsaccess=request.awsaccess,awskey=request.awskey,region=request.region );
result = AmazonS3listbuckets( datasource=request.datasource );
WriteDump(result);
WriteOutput(result.bucket[1]);
</cfscript>
For the above code I am getting this output:
Now I am adding one more function AmazonS3createbucket(),
<cfscript>
result = AmazonS3createbucket( datasource=request.datasource, bucket="anyBucket" );
</cfscript>
For the above script I am getting error: that No such function exists - amazons3createbucket.. Here's the screenshot:
I am referring the OpenBD Manual to filter these function.
Also faced the same problem while using this functions also:
<cfscript>
result = AmazonS3bucketexists( datasource=request.datasource, bucket="anyBucket" );
</cfscript>
Have you tried using an alternate syntax?
<cfscript>
result = AmazonS3bucketexists(ArgumentCollection = {
datasource : request.datasource,
bucket : "anyBucket"
});
</cfscript>

What's the best way to unit test a cfc that uses a Java object for a lot of its functionality?

I have a cfc that relies heavily on a Java object (created via JavaLoader) for a lot of its core functionality that I'd like to write some tests for and I'm not sure what the best way to do this is. Here is an example of a method I'd like to write a test for with instance.note being a java object.
<cffunction name="getNotes" returntype="Array" access="public" output="false" hint="I return a list of a users notebooks" >
<cfargument name="maxCount" type="numeric" required="false" default="9999" hint="The maximum number of notes to get" />
<cfscript>
if(arguments.maxCount)
return instance.note.listNotes(maxCount);
else
return instance.note.listNotes();
</cfscript>
</cffunction>
One thing I thought of doing is creating a stub CFC that has the same method names and similar return values and then mocking that stub and injecting it?
Can't you just write meaningful assertions on the result, i.e. on the array of notes? Looking at that code, the only things I'd test are a) when you pass a maxCount, does your resultant array honor that size? b) without maxCount, is the list of notes the length that you'd expect? Because that's all your code does. I'd test your code, not the code of the underlying java object.
When we needed to unit test CF functions that relied upon Java objects (which we did a LOT of), we used Mockito to mock the Java objects.
So, hoping this code snippet makes sense, its been almost a year since I've done this:
<cfcomponent displayname="TestWhatever" extends="mxunit.framework.TestCase" output="false">
<cffunction name="setUp" access="public" returntype="void">
<cfscript>
// named it mk for keeping it short
variables.mk = createObject("java","org.mockito.Mockito");
//Create the mock object
variables.mockNote = mk.mock(createObject("java","com.company.whatever.note").getClass());
// Mock Data
fullList = {whatever listNotes() returns}
partialList3 = {whatever listNotes(3) returns}
//some common mocking
mk.when(variables.mockNote.listNotes()).thenReturn(fullList);
mk.when(variables.mockNote.listNotes(mk.eq(3))).thenReturn(partialList3);
mk.when(variables.rootOrgObj.guid()).thenReturn("root");
// Assign the mock object to where your CFC expects it.
instance.note = variables.mockNote
</cfscript>
</cffunction>
</cfcomponent>
Having said that, if your sample function is real, there's really no point in unit testing it. Its simply not doing anything but being a proxy to the java object. There is no significant logic in there to test.
Since you have a default on the cfargument, it will always exist (again, I think so, its been a year since I've done CF), so your guard statement isn't even required - the first code path will always be called, with the passed maxCount if specified, or with 9999 if not.
I took Edward's answer and implemented it like so:
I used the JavaLoader library to create my mockito object.
variables.cfEvernote = "";
variables.classLoader = createObject("component", "resources.JavaLoader").
init(["#expandPath('../lib/mockito-all-1.8.5.jar')#",
"#expandPath('../lib/CFEvernote.jar')#",
"#expandPath('../lib/libthrift.jar')#",
"#expandPath('../lib/evernote-api-1.18.jar')#"]);
variables.mockito = variables.classLoader.create("org.mockito.Mockito").init();
Then in the setup method of my munit test I create my new mock java object:
<cffunction name="setUp" access="public" output="false" returntype="void">
<cfscript>
variables.cfEvernote = createObject("component","com.714studios.cfevernote.CFEvernote").
Init(variables.configArray[1],variables.configArray[2],
"sandbox.evernote.com",
"http://localhost/cfevernote/callback.cfm"
"#ExpandPath('../lib')#");
variables.mockCFEvernote = variables.mockito.mock(variables.classLoader.create("com.sudios714.cfevernote.CFEvernote").
Init("123","S1","232","sandbox.evernote.com","mock").getClass());
variables.cfEvernote.setCFEvernote(mockCFEvernote);
</cfscript>
Then in my tests I create my mock behavior like so.
<cffunction name="test..." returntype="void" access="public" output="false" >
<cfscript>
var notebooks = "";
var expected = 12;
var i = 0;
var retArray = createObject("Java","java.util.ArrayList");
var actual = "";
for(i = 1; i lte 12; i = i + 1){
retArray.Add("");
}
variables.mockito.when(mockCFEvernote.listNotebooks(12)).thenReturn(retArray);
notebooks = variables.cfEvernote.getNotebooks(12);
actual = arrayLen(notebooks);
assertEquals(expected,actual);
</cfscript>
I've also blogged about it in a bit more detail here - http://blog.bittersweetryan.com/2011/07/unit-testing-coldfusion-components-that.html.

How do I create a ColdFusion web service client that uses WS-Security?

I've exposed several web services in our product using Java and WS-Security. One of our customers wants to consume the web service using ColdFusion. Does ColdFusion support WS-Security? Can I get around it by writing a Java client and using that in ColdFusion?
(I don't know much about ColdFusion).
I'm assuming you mean you need to pass the security in as part of the SOAP header. Here's a sample on how to connect to a .Net service. Same approach should apply w/ Java, just the url's would be different.
<cfset local.soapHeader = xmlNew()>
<cfset local.soapHeader.TheSoapHeader = xmlElemNew(local.soapHeader, "http://someurl.com/", "TheSoapHeader")>
<cfset local.soapHeader.TheSoapHeader.UserName.XmlText = "foo">
<cfset local.soapHeader.TheSoapHeader.UserName.XmlAttributes["xsi:type"] = "xsd:string">
<cfset local.soapHeader.TheSoapHeader = xmlElemNew(local.soapHeader, "http://webserviceUrl.com/", "TheSoapHeader")>
<cfset local.soapHeader.TheSoapHeader.Password.XmlText = "bar">
<cfset local.soapHeader.TheSoapHeader.Password.XmlAttributes["xsi:type"] = "xsd:string">
<cfset theWebService = createObject("webservice","http://webserviceUrl.com/Webservice.asmx?WSDL")>
<cfset addSOAPRequestHeader(theWebService, "ignoredNameSpace", "ignoredName", local.soapHeader, false)>
<cfset aResponse = theWebService.SomeMethod(arg1)>
Hope this is what you needed.
This is probably more accurate to produce the 'simple' xml soap header. The example above is missing a few lines.
Local['soapHeader'] = xmlNew();
Local['soapHeader']['UsernameToken'] = xmlElemNew(local.soapHeader, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "UsernameToken");
Local['soapHeader']['UsernameToken']['username'] = xmlElemNew(local.soapHeader, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "username");
Local['soapHeader']['UsernameToken']['username'].XmlText = Arguments.szUserName;
Local['soapHeader']['UsernameToken']['username'].XmlAttributes["xsi:type"] = "xsd:string";
Local['soapHeader']['UsernameToken']['password'] = xmlElemNew(local.soapHeader, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "password");
Local['soapHeader']['UsernameToken']['password'].XmlText = Arguments.szPassword;
Local['soapHeader']['UsernameToken']['password'].XmlAttributes["xsi:type"] = "xsd:string";
addSOAPRequestHeader(ws, "ignoredNameSpace", "ignoredName", Local.soapHeader, false);
I've never done any ws-security, and don't know if ColdFusion can consume it or not, but to answer your secondary question:
Can I get around it by writing a java client and using that in coldfusion?
Yes, absolutely. ColdFusion can easily use Java objects and methods.