So I’ve often wondered how builtin tags like cfsavecontent, cfdocument, cfquery, etc work; being able to grab the text generated inside the tags.
Well the other day I was working on letting users download files out of a zipped archive, by streaming the decompressed data straight to the browser, which I did using getPageContext to get the output stream.
Afterwards it occurred to me that this must be how tags like cfsavecontent working, just inverted; that they’re changing the output stream so the generated text goes to the stream and into a variable, rather than getting written to the stream that goes to the user’s browser.
So my question is, is there a way for us plebeian programmers to do this as well? Obviously we would have to instantiate a java outputstream (no issues there). But I can’t find where I would set my stream as the one where lucee writes the output too.
EDIT: So of course none of the guides I’ve read up till now on custom tags have mentioned it, but right after I make this post I find one which mentions thisTag.generatedContent Figures.
I would however still like to pose the same question to the lucee devs, because it would just be really, really useful: is there a way to set the outputstream that generated content is written to? Either for a custom tag, or just the a regular cfm script.
It looks like there is suppose to be a way, but for the life of me I could not get it to freaking work.
Based on the documentation here, we should be able to do something like the following:
<cfscript>
strwrt = createObject('java', 'java.io.StringWriter').init();
ctx = getPageContext();
body = ctx.pushBody(strwrt);
// output should now be written to the writer we provided and it's output stream
writeOutput('Test');
ctx.popBody();
// output should not go back to the previous output stream
</cfscript>
However it would never go to the writer I provided. I played around with a bunch of different variations of the above with no success.
You can use the underlying page context to capture the output data, but basically you’re just replicating cfsavecontent, as it just saves it to a variable in memory, which is okay, provided you don’t need scalability.
So looking at the Lucee code, they didn’t implement pushBody(Writer writer), which is why it doesn’t work. They have their own pushBody(), which deals with the BodyContentStack object and there does not appear to be a way to change this.
@micstriit is there any way to send the output to a file instead of the output buffer? For example, what if we wanted to capture all output between a start and end tag to go into a file, but stream the content to the file to avoid building a large StringBuilder.
And while the <cffile /> tag does allow a body, it builds the body of the tag as string and then writes that to the file once it hits the end of the file tag. It does not send the OutputStream to a file.
For the problem I’m trying to solve, the page request could end up generating huge amounts of HTML, so I’m trying to stream the output as it’s being generated to a file, which I will then have the webserver send. That way the entire output never needs to be held in memory.
If I could redirect the response OutputStream to a file it would drastically simplify what I’m trying to do. Otherwise, there’s going to need to be a huge amount of refactoring of the code to generate the output manually just so I can append everything to a file.