Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 25, 2014 11:40
Communicating With The Client Whilst Inside A ColdFusion Custom Tag
<!---
Since we are waiting for our thread in parallel, let's increase
the page timeout to ensure we can process the response.
--->
<cfsetting requesttimeout="60" />
<!--- I am the name of the thread we are going to execute. --->
<cfset request.threadName = "testThread#randRange( 1111, 9999 )#" />
<!---
I am the status variable (this is what the thread is going to
update as it processes).
--->
<cfset request[ "#request.threadName#_status" ] = "-" />
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!---
Define a function that we will pass into the thread to allow it
to report progress without having to know much about how this is
being echoed to the client.
--->
<cffunction
name="reportProgress"
access="public"
returntype="string"
output="false"
hint="I report the progress of the given thread.">
<!--- Define arguments. --->
<cfargument
name="status"
type="string"
required="true"
hint="I am the status of the calling context."
/>
<!--- Update the progress status in the request. --->
<cfset request[ "#request.threadName#_status" ] = arguments.status />
<!--- Return out. --->
<cfreturn />
</cffunction>
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!---
Launch our async thread. Notice that we are passing a reference
to our status update method into thread for it to use.
NOTE: We are also passing-in the file name that we want the given
thread to output it's content to.
--->
<cfthread
name="#request.threadName#"
action="run"
reportprogress="#reportProgress#"
filename="#expandPath( './#createUUID()#.txt' )#">
<!---
Pass controll off to the custom tag. Notice that with a
custom tag, we cannot typically FLUSH output to the client
during its execution. However, since this is executing inside
a CFThread, it is now in a differnt output buffer.
--->
<cf_generatedocument
filename="#attributes.fileName#"
reportprogress="#attributes.reportProgress#"
/>
<!---
Once the document has been gernated, let's just store the
file name back in the thread result so the parent page can
reference it.
--->
<cfset thread.fileName = attributes.fileName />
</cfthread>
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<cfoutput>
<!DOCTYPE html>
<html>
<head>
<title>Your Document Is Being Generated</title>
</head>
<body>
<h1>
Your Document Is Being Generated
</h1>
<p>
Progress:
<span id="documentProgrress">
<!--- This is where we will output updates. --->
</span>
</p>
<!--- --------------------------------------------- --->
<!--- --------------------------------------------- --->
<!--- --------------------------------------------- --->
<!--- --------------------------------------------- --->
<!--- Flush the current output to the client. --->
<cfflush />
<!--- Get the current status. --->
<cfset currentStatus = "" />
<!--- Keep looping until the thread is finished. --->
<cfloop condition="(cfthread[ request.threadName ].status neq 'COMPLETED')">
<!--- Check to see if the status has been updated. --->
<cfif (currentStatus neq request[ "#request.threadName#_status" ])>
<!--- Update the document using the status variable. --->
<script type="text/javascript">
document
.getElementById( "documentProgrress" )
.innerHTML = "#request[ '#request.threadName#_status' ]#"
;
</script>
<!---
Save the current status so we don't get duplicate
output (in the source code).
--->
<cfset currentStatus = request[ "#request.threadName#_status" ] />
<!---
Flush the update to the client so the document
actually updates.
--->
<cfflush />
<!---
Sleep for a few milliseconds to let the document
have a change to update.
--->
<cfthread
action="sleep"
duration="100"
/>
</cfif>
</cfloop>
<!---
Now that the thread has completed, forward user to the
generated file.
--->
<script type="text/javascript">
location.href = "#getFileFromPath( cfthread[ request.threadName ].fileName )#";
</script>
</body>
</html>
</cfoutput>
<!--- Param the tag attributes. --->
<cfparam
name="attributes.fileName"
type="string"
/>
<cfparam
name="attributes.reportProgress"
type="any"
/>
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- Create a buffer to hold our file content. --->
<cfset buffer = [] />
<!--- Create 1000 lines in our file. --->
<cfloop
index="lineIndex"
from="1"
to="1000"
step="1">
<!--- Append the line to the buffer. --->
<cfset arrayAppend(
buffer,
"Hey this is line #lineIndex# of my file!"
) />
<!--- Report progress. --->
<cfset attributes.reportProgress(
"#lineIndex# lines have been written to this file."
) />
<!---
To mimic something that is actually processing intensive,
sleep this thread for a bit.
--->
<cfthread
action="sleep"
duration="5"
/>
</cfloop>
<!--- Report file completion. --->
<cfset attributes.reportProgress(
"Your file has been successfully generated!"
) />
<!--- Write the content to file. --->
<cfset fileWrite(
attributes.fileName,
arrayToList( buffer, (chr( 13 ) & chr( 10 )) )
) />
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- Exist out of tag. --->
<cfexit method="exitTag" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment