Binary Content in WDDX Packets


So far, the WDDX packets you've seen in this chapter have contained data that can be expressed as a string. Sure, the data might be arranged in complex data structures such as arrays, recordsets, and structures, and the data might contain dates or numbers surrounded by <number> or <dateTime> elements. But the individual pieces of data have all been easy for WDDX to express as a string between the actual <number>, <dateTime>, <string>, and other data elements.

WDDX also allows you to include binary data in packets. For purposes of this discussion, binary data means any data that doesn't have an obvious plain-text counterpart. The most obvious examples of binary data are the contents of nontextual files such as image files, database files, executables, Word or other application-specific documents, and so on. Basically, any file that shows up as "garbage" in a text editor (such as Macromedia Dreamweaver or Windows Notepad) should probably be considered to be a binary file for our purposes here.

You may not be very familiar with the binary object type in CFML because it's not often needed in Web applications. Here are a few notes about binary objects in ColdFusion:

  • You can use <cffile> with action="ReadBinary" to read a binary file. The contents are returned to you as a binary object variable.

  • Once you have a binary object variable, you can save it to the server's drive using the usual <cffile> tag with action="Write".

  • You can check whether a particular variable holds a binary object by using the IsBinary() function, or IsObject("binary").

  • A binary object variable is essentially an array of individual bytes. There wouldn't normally be much of a reason to, but you can access the individual bytes using normal array notation, such as MyBinary[5] to access the fifth byte. As you might expect, ArrayLen(MyBinary) returns the size of the object in bytes. If MyBinary came from a file using <cffile> with action="ReadBinary", then ArrayLen(MyBinary) should match the size of the file on disk as reported by the operating system.

If you serialize a binary object with <cfwddx>, or if you serialize a recordset, array, or structure that contains a binary object, the object will be represented by a pair of <binary> elements in the resulting WDDX packet. Between the <binary> elements will be bunch of characters that look like some kind of encrypted or scrambled text. There will also be a length attribute that indicates the number of characters between the <binary> elements, something like this:

 <binary length="6614">R0lGODlhZACDANUAAP//zP//mf//M///AP/MzP/Mm...</binary> 

I replaced most of the characters with the ... at the end, but you get the idea. To us humans, it looks like a bunch of random text characters.

What's going on here? Well, the binary data has been encoded (converted) into a special text format called Base 64. Base 64 encoding is most often used for email attachments: Your email client converts your attached documents and images to this Base 64 format and includes the Base 64 version in the message. That's why binary files can be sent in email messages, which otherwise know only how to deal with plain text.

You can find out more about Base 64 on the Web (start at the W3C site), but it's not really so important for you to understand the mechanics of the encoding. All you need to know is that it's possible to include binary content in your WDDX packets. (And if anyone asks, you can tell them that it's done by converting to Base 64 so that the content can be represented in XML, which is only capable of handling text characters.)

NOTE

ColdFusion also provides ToBinary() and ToBase64() functions so that you can convert between Base 64 text and individual pieces of binary data. These functions have nothing directly to do with WDDX per se; however, the same conversions are used internally by <cfwddx> when you serialize or deserialize packets that contain binary data.


Listing 16.18 shows a more advanced version of the Films Robot page that was created earlier in Listing 16.13. Among other things, this version supports an optional Images parameter that can be included in the URL. If Images=Yes, then the images (for those films that have them) are included in the WDDX packet that the page generates.

Listing 16.18. FilmsRobot2.cfmIncluding Binary Content in WDDX Packets
 <!--- Name:        FilmsRobot2.cfm Author:      Nate Weiss and Ben Forta Description: Creates a back-end web page that              supplies data about films Created:     02/01/05 ---> <!--- If a WDDX packet is supplied to this page as a FORM or URL parameter ---> <cfif IsDefined("ParamsAsWDDX")>   <!---   Deserialize the packet and use its contents as parameters   to control this page's behavior   --->   <cfwddx action="WDDX2CFML"           input="#ParamsAsWDDX#"           output="IncomingParams"> <cfelse>   <!---   The values in this structure will be used to control page's behavior   --->   <cfset IncomingParams=StructNew()> </cfif> <!--- If form parameters are being submitted, use them to contol behavior ---> <cfif StructCount(FORM) GT 1>   <cfset StructAppend(IncomingParams, FORM, "Yes")>   <cfset StructDelete(IncomingParams, "FIELDNAMES")> <!--- Otherwise, just use  ---> <cfelse>   <cfset StructAppend(IncomingParams, URL, "Yes")> </cfif> <!--- The incoming WDDX packet may include these parameters. The default values are used when there is no incoming packet, or when the incoming packet doesn't include the parameter. ---> <cfparam name="IncomingParams.usecache" type="boolean" default="yes"> <cfparam name="IncomingParams.details" type="boolean" default="no"> <cfparam name="IncomingParams.filmid" type="numeric" default="0"> <cfparam name="IncomingParams.keywords" type="string" default=""> <cfparam name="IncomingParams.orderby" type="string" default="movietitle"> <cfparam name="IncomingParams.images" type="boolean" default="no"> <!--- If a cached query may be used ---> <cfif IncomingParams.UseCache>   <cfset CachedWithin=CreateTimeSpan(0,0,30,0)> <cfelse>     <cfset CachedWithin=CreateTimeSpan(0,0,0,0)> </cfif> <!--- Execute a database query to select film information from database ---> <cfquery name="FilmsQuery"           datasource="ows"           cachedwithin="#CachedWithin#">   SELECT     <!--- If all information about film(s) is desired --->     <cfif IncomingParams.Details>       *     <!--- Otherwise, return the film's ID and title --->       <cfelse>       FilmID, MovieTitle        <cfif IncomingParams.Images>, ImageName</cfif>     </cfif>   FROM Films   <!--- If a specific film ID was specified --->   <cfif IncomingParams.FilmID GT 0>     WHERE FilmID = #IncomingParams.FilmID#   <!--- If keywords were provided to search with --->     <cfelseif IncomingParams.Keywords NEQ "">       WHERE MovieTitle LIKE '%#IncomingParams.Keywords#%'   </cfif>   <!--- Order the results appropriately --->   ORDER BY #IncomingParams.OrderBy# </cfquery> <!--- If the requesting process wants images included in the packet... ---> <cfif IncomingParams.Images>   <!--- Add an ImageContent column to the query recordset --->   <cfset QueryAddColumn(FilmsQuery, "ImageContent", ArrayNew(1))>      <!--- For each row in the recordset --->   <cfloop query="FilmsQuery">     <!--- If this film has an associated image           (according to the database) --->     <cfif ImageName NEQ "">       <!--- Location of the image on the server's drive --->       <cfset ImagePath=ExpandPath("/ows/images/#ImageName#")>              <!--- If the file actually exists on the server --->       <cfif FileExists(ImagePath)>         <!--- Read the contents of the file --->         <!--- ImageBinary will be a binary object variable --->         <cffile action="READBINARY"                 file="#ImagePath#"                 variable="ImageBinary">                  <!--- Store the binary object variable in               the ImageContent column --->           <cfset FilmsQuery.ImageContent[CurrentRow] = ImageBinary>         </cfif>            </cfif>   </cfloop> </cfif> <!--- Convert the query recordset to a WDDX packet ---> <cfwddx action="CFML2WDDX"         input="#FilmsQuery#"         output="WDDXPacket">    <!--- Return the packet to whatever system requested this page. Use <cfcontent> to reset the output stream, so that any whitespace, page headers, etc., included by Application.cfm gets discarded. ---> <cfcontent type="text/xml"             reset="Yes"><cfoutput>#WDDXPacket#</cfoutput> 

The new portion of the listing is the <cfif> block in the middle. If there is an Images=Yes parameter in the URL, a new query column called ImageContent is added to the FilmsQuery recordset. Then, for each film that has an associated image, the <cffile> tag is used with action="ReadBinary" to read the actual contents of the file into ImageBinary (a binary object variable). The value of ImageBinary is then placed into the ImageContent column of the recordset. When this block of code is finished, the query object contains binary data for each film that has an associated image. The resulting WDDX packet will thus contain <binary> blocks (like the one shown earlier in this section) for each image.

There are a few other differences between this version of the robot page and the original from Listing 16.13:

  • The system that is submitting the request to the robot can now specify the sort order with the OrderBy parameter, and with the UseCache parameter can control whether ColdFusion can use cached query information to build the packet.

  • The <cfcontent> tag is used to specify a content type of text/xml as the packet is being sent back to whatever system requested the page. Since WDDX packets are XML, the addition of the content type is appropriate. However, the <cfwddx> tag and the other WDDX implementations discussed in this chapter will work just fine regardless of the content type.

Listing 16.19 demonstrates use of the binary data in a WDDX packet after the packet has been deserialized. This is a revised version of the film detail page from Listing 16.17 (shown in Figure 16.8).

Listing 16.19. UseFilmsRobot2b.cfmUsing Binary Data Included in WDDX Packets
 <!--- Name:        UseFilmsRobot2b.cfm Author:      Nate Weiss and Ben Forta Description: Fetches a WDDX packet via              HTTP and uses the query              contained within Created:     02/01/05 ---> <!--- We need an ID number for the desired film ---> <cfparam name="URL.FilmID"          type="numeric"> <!--- Location of the robot page ---> <!--- The URL could be anywhere in world, not just on this server ---> <cfset RobotURL="http://localhost:8500/ows_adv/16/FilmsRobot2.cfm"> <!--- Add parameters so the robot knows to return detailed information ---> <cfset RobotURL=RobotURL&"?FilmID=#URL.FilmID#&Details=Yes&Images=Yes"> <!--- Contact the robot page and retrieve the WDDX packet it returns ---> <cfhttp method="Get"         url="#RobotURL#"> <!--- Deserialize the packet, which we know holds a query recordset --->   <cfwddx action="WDDX2CFML"         input="#CFHTTP.FileContent#"         output="FilmQuery"> <!--- If there is binary image content for this film --->   <cfif IsBinary(FilmQuery.ImageContent)>   <!--- Folder location for storing deserialized images --->   <cfset DeserializedImageFolder=ExpandPath("DeserializedImages")>   <!--- Create the folder if it doesn't already exist --->   <cfif NOT DirectoryExists(DeserializedImageFolder)>     <cfdirectory action="Create"                  directory="#DeserializedImageFolder#">   </cfif>      <!--- The image will be saved using the name in the ImageName column --->   <cfset ImageFilePath=DeserializedImageFolder & "/" & FilmQuery.ImageName>   <!--- Save image to DeserializedImages folder on the server's drive --->   <cffile action="WRITE"           file="#ImageFilePath#"           output="#FilmQuery.ImageContent#"> </cfif>    <!--- We can now use the query object normally, just as if it came directly from a <cfquery> tag. ---> <cfoutput query="FilmQuery">   <h2>#MovieTitle#</h2>   #PitchText#     <p><strong>Summary:</strong><br>   #Summary#<br>     <p><strong>Budget:</strong><br>   #LSCurrencyFormat(AmountBudgeted)#<br>   <p><strong>Date In Theaters:</strong><br>   #LSDateFormat(DateInTheaters)#<br>   <!--- If there is an image available --->     <cfif ImageName NEQ "">     <p><strong>Image:</strong><br>     <img src="/books/2/449/1/html/2/DeserializedImages/#ImageName#" border="0">   </cfif> </cfoutput> 

When running this code be sure to pass a FilmID as a URL parameter (for example, ?FilmID=2).

This version adds Images=Yes to the URL it uses to contact the robot page, which means the resulting packet may contain a <binary> element for the selected film's image. After the packet is deserialized, the <cfif> block in the middle of the listing checks to see if the ImageContent field actually contains binary data. If so, the binary content is stored as an image on the server's drive, in a folder called DeserializedImages within the folder in which this listing is saved. (If the folder does not exist yet, it is created with <cfdirectory>.)

Finally, near the end of the code, an ordinary <img> tag is used to display the image for the film, if available. The result looks just like the image in Figure 16.8, with the addition of the film's image at the bottom of the page.

Using binary data with WDDX in this way doesn't make a whole lot of sense if this page is on the same server as the robot page. But if the robot server is on a different server, any needed images will be automatically copied to the current server as the page executes. Of course, even this scenario doesn't make much sense in the context of the Web, because you could just tell the browser to access the images on the robot server without copying from one place to anotherbut you get the idea. The binary support in WDDX allows you to include any kind of data in packets, including images, documents, and so on. What you do with the feature is up to you.

Listing 16.20 shows another way to produce the same results. With this version, the binary content is not saved to a permanent location on the server's drive. Instead, the content is stored at a temporary location supplied by GetTempFile() and then streamed to the browser with <cfcontent>. The <img> tag at the bottom of the listing has been modified to request the image directly from the <cfcontent> part of this listing, rather than as a discrete .gif file. In other words, ColdFusion supplies the HTML for the details page (see Figure 16.8) as well as the actual image to display on the page. All the information comes from the robot page, which could be on the other side of the globe.

Listing 16.20. UseFilmsRobot2c.cfmServing Binary Content from a WDDX Content Directly
 <!--- Name:        UseFilmsRobot2c.cfm Author:      Nate Weiss and Ben Forta Description: Fetches a WDDX packet via              HTTP and uses the query              contained within Created:     02/01/05 ---> <!--- We need a film ID to be supplied in the URL ---> <cfparam name="URL.FilmID"          type="numeric"> <!--- Flag used to indicate whether browser is requesting the the film's image to display on the detail page, or the detail page itself. ---> <cfparam name="URL.ImageOnly"          type="boolean"          default="No"> <!--- Location of the robot page ---> <!--- The URL could be anywhere in world, not just on this server ---> <cfset RobotURL="http://localhost:8500/ows_adv/16/FilmsRobot2.cfm"> <!--- Add parameters so the robot knows to return detailed information ---> <cfif URL.ImageOnly>   <cfset RobotURL=RobotURL&"?FilmID=#URL.FilmID#&Details=No&Images=Yes"> <cfelse>   <cfset RobotURL=RobotURL&"?FilmID=#URL.FilmID#&Details=Yes&Images=No"> </cfif> <!--- Contact the robot page and retrieve the WDDX packet it returns ---> <cfhttp method="Get"         url="#RobotURL#"> <!--- Deserialize the packet, which we know holds a query recordset --->   <cfwddx action="WDDX2CFML"         input="#CFHTTP.FileContent#"         output="FilmQuery"> <!--- If the ImageOnly flag is set, send back the binary image itself ---> <cfif URL.ImageOnly>   <!---   If there is binary image content for this film   --->     <cfif IsBinary(FilmQuery.ImageContent)>     <!--- Temporary location for the image --->     <cfset TempFile=GetTempFile(GetTempDirectory(), "img")>          <!--- Save the image content from the WDDX packet to the temp file --->     <cffile action="WRITE"             file="#TempFile#"             output="#FilmQuery.ImageContent#">     <!---     Stream the content to the browser.     The temporary file will be deleted when finished.     --->     <cfcontent type="image/gif"                reset="Yes"                file="#TempFile#"                deletefile="Yes">   <!--- If we are meant to send back image, but robot didn't provide it --->   <cfelse>     <!--- Log the problem --->     <cflog text="The image for FilmID #URL.FilmID# was not recieved."            file="FilmsRobot"            type="Information">              </cfif> <!--- When the page is called without the ImageOnly flag, we should create the detail page for the film (as HTML). ---> <cfelse>   <!---   We can now use the query object normally,   just as if it came directly from a <cfquery> tag.   --->   <cfoutput query="FilmQuery">     <h2>#MovieTitle#</h2>     #PitchText#     <p><strong>Summary:</strong><br>     #Summary#<br>     <p><strong>Budget:</strong><br>     #LSCurrencyFormat(AmountBudgeted)#<br>     <p><strong>Date In Theaters:</strong><br>     #LSDateFormat(DateInTheaters)#<br>        <!--- If there is an image available --->       <cfif ImageName NEQ "">       <p><strong>Image:</strong><br>              <!---       The SRC for image is this page's URL with ImageOnly flag added.       The browser will call this page again to get the image itself.       --->       <img src="/books/2/449/1/html/2/UseFilmsRobot2c.cfm?FilmID=#URL.FilmID#&ImageOnly=Yes"            border="0">     </cfif>   </cfoutput>      </cfif> 



Advanced Macromedia ColdFusion MX 7 Application Development
Advanced Macromedia ColdFusion MX 7 Application Development
ISBN: 0321292693
EAN: 2147483647
Year: 2006
Pages: 240
Authors: Ben Forta, et al

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net