Cooking with ASP.NET, Part 2
Pages: 1, 2
Recipe 18.6: Saving and Reusing HTML Output
Problem
To improve the performance of pages that rarely change, you want to capture the output of those pages and save it for reuse when the page is requested.
Solution
Create the page that contains the desired content just as you would
any other page, including the server controls you need. At the end of
the Page_Load method, use the
RenderControl method of the
Page control to generate the HTML and then save
the HTML to a file.
In the code-behind class for the page, use the .NET language of your choice to:
-
Create an
HtmlTextWriterto use for rendering the page. -
Use the
RenderControlmethod of thePagecontrol to render the output of the page to theHtmlTextWriter. -
Save the rendered output to a file and redirect to another page.
Example 18-16 and Example 18-17 show the VB and C# code-behind files for our application that demonstrates this solution.
Discussion
Occasionally, it's beneficial to save the HTML output from a generated page. This is commonly done when using the saved HTML can significantly improve web site performance. If the content of a page is static, for example, there is no point in dynamically generating HTML each time the page is requested. Until the advent of ASP.NET, the only way to save the HTML was to use the "Save as Complete Web Page" feature of Internet Explorer or another browser. Although this method does save the HTML, it also copies all of the page images to the local machine and changes the image references to point to the local copies. If you are trying to improve performance by capturing a static copy of the page to use on your web server, this technique does not work very well.
With ASP.NET you can easily capture the HTML exactly as it would be
sent to the browser. For our example that illustrates this solution,
we have used the page from Recipe 18.3
and added code to the Page_Load method to save the
rendered output.
The RenderControl method of the
Page control provides the ability to render the
output of the page to the HtmlTextWriter passed to
the method. Unfortunately, the HtmlTextWriter does
not provide any methods for reading the contents, so a little more
work is required to access the rendered HTML.
By first creating a StringBuilder and then using
it to create a StringWriter, which is then used to
create the required HtmlTextWriter, the contents
of the HtmlTextWriter are available by way of the
original StringBuilder. This works because the
underlying storage mechanism for the StringWriter
is a StringBuilder; and because the
StringWriter (a stream) is used to create the
HtmlTextWriter, the
RenderControl method is actually writing the
rendered output to the StringBuilder. Our example
code to accomplish this is shown here:
renderedOutput = New StringBuilder( )
strWriter = New StringWriter(renderedOutput)
tWriter = New HtmlTextWriter(strWriter)
renderedOutput = new StringBuilder( );
strWriter = new StringWriter(renderedOutput);
tWriter = new HtmlTextWriter(strWriter);
After creating the HtmlTextWriter, the
RenderControl method of the
Page is called to render the HTML for the page:
Page.RenderControl(tWriter)
Page.RenderControl(tWriter);
Now that the rendered HTML is available, it needs to be saved to a
file on the server. This can be accomplished by creating the file
with a FileStream and using a
StreamWriter to write the rendered output in the
StringBuilder to the file, as shown here:
filename = Server.MapPath(".") & "\" & OUTPUT_FILENAME
outputStream = New FileStream(filename, _
FileMode.Create)
sWriter = New StreamWriter(outputStream)
sWriter.Write(renderedOutput.ToString( ))
sWriter.Flush( )
filename = Server.MapPath(".") + "\\" + OUTPUT_FILENAME;
outputStream = new FileStream(filename,
FileMode.Create);
sWriter = new StreamWriter(outputStream);
sWriter.Write(renderedOutput.ToString( ));
sWriter.Flush( );
The last step is to redirect to another page. This is necessary
because allowing the page to be displayed would result in an
additional rendering and an exception being thrown indicating the
page has more than one server-side form element. If you need the page
to be displayable anyway, a parameter can be passed in the
querystring and checked in the code to determine
if the output should be rendered and written to a file or handled
normally.
Response.Redirect([next page])
Response.Redirect([next page]);
This technique can be used for individual controls in the same manner
as for the entire page. For example, if you have a page that contains
a DataGrid and you want the rendered HTML for just
the DataGrid, you can call the
RenderControl method of the
DataGrid and then save the output as described
earlier.
See Also
Recipe 18.3
Example 18-16. Capturing rendered output (.vb)
Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Const OUTPUT_FILENAME As String = "CH18CaptureRenderedOutputVB.html"
Dim renderedOutput As StringBuilder
Dim strWriter As StringWriter
Dim tWriter As HtmlTextWriter
Dim outputStream As FileStream
Dim sWriter As StreamWriter
Dim filename As String
Dim nextPage As String
Try
'set the names of the XML and XSLT documents used in the
'transformation
xmlTransform.DocumentSource = "xml/books.xml"
xmlTransform.TransformSource = "xml/books.xslt"
'create a HtmlTextWriter to use for rendering the page
renderedOutput = New StringBuilder
strWriter = New StringWriter(renderedOutput)
tWriter = New HtmlTextWriter(strWriter)
'render the page output
Page.RenderControl(tWriter)
'save the rendered output to a file
filename = Server.MapPath(".") & "\" & OUTPUT_FILENAME
outputStream = New FileStream(filename, _
FileMode.Create)
sWriter = New StreamWriter(outputStream)
sWriter.Write(renderedOutput.ToString( ))
sWriter.Flush( )
'redirect to another page
'NOTE: Continuing with the display of this page will result in the
' page being rendered a second time which will cause an exception
' to be thrown
nextPage = DisplayMessage.PAGE_NAME & "?" & _
DisplayMessage.QS_PAGE_HEADER & "=Information" & "&" & _
DisplayMessage.QS_MESSAGE_LINE1 & "=HTML Output Saved To " & _
OUTPUT_FILENAME
Response.Redirect(nextPage)
Finally
'clean up
If (Not IsNothing(outputStream)) Then
outputStream.Close( )
End If
If (Not IsNothing(tWriter)) Then
tWriter.Close( )
End If
If (Not IsNothing(strWriter)) Then
strWriter.Close( )
End If
End Try
End Sub 'Page_Load
Example 18-17. Capturing rendered output (.cs)
private void Page_Load(object sender, System.EventArgs e)
{
const string OUTPUT_FILENAME = "CaptureRenderedOutput_VB.html";
StringBuilder renderedOutput = null;
StringWriter strWriter = null;
HtmlTextWriter tWriter = null;
FileStream outputStream = null;
StreamWriter sWriter = null;
String filename = null;
String nextPage = null;
try
{
// set the names of the XML and XSLT documents used in the
// transformation
xmlTransform.DocumentSource = "xml//books.xml";
xmlTransform.TransformSource = "xml//books.xslt";
// create a HtmlTextWriter to use for rendering the page
renderedOutput = new StringBuilder( );
strWriter = new StringWriter(renderedOutput);
tWriter = new HtmlTextWriter(strWriter);
// render the page output
Page.RenderControl(tWriter);
// save the rendered output to a file
filename = Server.MapPath(".") + "\\" + OUTPUT_FILENAME;
outputStream = new FileStream(filename,
FileMode.Create);
sWriter = new StreamWriter(outputStream);
sWriter.Write(renderedOutput.ToString( ));
sWriter.Flush( );
// redirect to another page
// NOTE: Continuing with the display of this page will result in the
// page being rendered a second time which will cause an exception
// to be thrown
nextPage = DisplayMessage.PAGE_NAME + "?" +
DisplayMessage.QS_PAGE_HEADER + "=Information" + "&" +
DisplayMessage.QS_MESSAGE_LINE1 + "=HTML Output Saved To " +
OUTPUT_FILENAME;
Response.Redirect(nextPage);
}
finally
{
// clean up
if (outputStream != null)
{
outputStream.Close( );
}
if (tWriter != null)
{
tWriter.Close( );
}
if (strWriter != null)
{
strWriter.Close( );
}
}
} // Page_Load
Mikkel Aaland is a professional photographer whose pioneering work in digital photography dates back to 1981. He is the author of nine books including the bestselling Photoshop Elements Solutions and O'Reilly's acclaimed Photoshop Lightroom Adventure. Visit his website at http://www.shooting-digital.com.
Return to ONDotnet.com

