HTML Images and Binary Fields
by Michael Nuwer

 
   

Introduction

How do I get a binary field (i.e. images stored in a table) to appear in a web page created by my dBL Web application? This is a question that has been asked many times in the dBASE Internet Newsgroup. The basic problem, as Ken Chan notes, is that there is no good way to include the image data inline with the HTML. This is because the HTML IMG tag refers to an image file stored on the Web server and the browser will ask that server for each image. In the context of a dBL CGI application, by the time the browser requests an image, the server has long forgotten any association with the desired binary field.

Ken Chan offers a solution to this problem as part of his dBL Template system. This system is a dBL implementation of a scripting language similar to ASP, JSP or PHP. It is not necessary, however, to make use of the entire template system in order to use the part that delivers images to a web browser. I spent about ninety minutes one weekend hacking Ken Chanís code and came up with a marvelously simple way to deliver images from a binary field to a web browser. Ken Chanís code is truly splendid, which, I am sure, is the reason it was so easy for me to hack.

The dBL Template system is available at:
http://www.nuwermj.potsdam.edu/dbasecodelib/dblt_by_KenChan.zip

Setup

  1. Unzip this archive file to a folder on your development machine.
  2. Start dBASE and compile dBLTImages.prg.
  3. Build dBLTImages.pro into an EXE file (in the command window type: Build dBLTImages WEB)
  4. Compile dQWebClass.cc. This file is a copy of the file found in <dBASE Home>\bin\dQuery folder.
  5. Finally, compile HTMLImages.cc. This is the class that you will use in your CGI applications. It is a subclass of dQWebClass.cc and adds functionality for streaming a unique image identifier.
These three files are the primary components needed to deliver binary field images to a web browser. The remaining files are examples that will be explored in the remainder of this article.

How does it work?

Normally an image file (gif or jpg) is stored on a web server and an HTML page includes an image tag that points to the file. The image tagís SRC property contains the location of the image.
 
 
<IMG SRC="/images/SomeImage.gif">
   

Ken Chanís system replaces the URL pointing to the image with a URL pointing to a dBL CGI application. Something like the following:
 
 
<IMG SRC="/cgi-bin/dbltimages.exe?95838892824">
   

There are two processes that make this system work.

  1. When the developer pragmatically constructs an HTML page that contains DBF stored images, they must insert the correct image identifiers into the SRC image tag property. The developer must write a bit of code to do this.
  2. When the browser calls dbltimages.exe, the actual image must be streamed back to the browser. Ken Chanís program handles this part.
An example

Among the sample files that accompany this article, you will find a dBASE table named Inventory.dbf (with index and binary field files) and a program file named TestApp.prg. We will consider the code in the program files. If you want to stream a Web page that contains images stored in a binary field, your program code will contain the following elements. The primary code segment is listed here:
 
 
01   Set proc to HTMLImages.cc additive 
02   oCGI = new CGIImages() 
03   oCGI.Connect() 
04   cTable = "INVENTORY.DBF" 
05   q = new query() 
06   q.sql = [Select * from "] + cTable + ["] 
07   q.active = true 
08   q.rowset.indexName = "Item ID" 
09   oCGI.StreamHeader("HTML page with images") 
10   q.rowset.first()
11   do
12      oCGI.fOut.puts([<P>Item ID: ] + q.rowset.fields['item id'].value )
13      cImage = oCGI.binaryFieldImage( q.rowset.fields['Picture'].value )
14      oCGI.fOut.puts( [<P><IMG SRC="]+ cImage + [" ALT="">] ) 
15      oCGI.fOut.puts([<HR>])
16   until not q.rowset.next()
17   oCGI.StreamFooter()
   

Line 01 loads HTMLImages.cc into memory. This file contains a class definition that is subclassed from dQWebClass.cc. In addition to all the functionally of the dBASE Web Classes, support is added for creating a unique identifier for the image.

Line 02 creates a new instance of the CGIimage class.

Line 03 makes a connection to the Web Server. This is a standard line for all CGI applications that use the dBASE Web classes.

Lines 04 through 08 open and configure a query object. This example is using a table named Inventory.dbf, which contains images in a field named picture.

Line 09 streams header information for the HTML page. StreamHeader() is a method in the dBASE Web classes and is a common element of CGI application.

Line 10 moves the row point to the first row.

Line 11 begins a do loop. The program will loop through the Inventory table and stream an image tag for each row. Normally, itís a good idea to format your web report with the HTML table object. This technique gives the developer the most control over the pageís layout. In this example, however, I am trying to focus on a single element and, in order to keep this as simple as possible, I have omitted a table object.

Line 12 streams the value stored in the Item ID field of the inventory table.

Line 13 is the most important with respect to the system we are describing. This line calls a method named binaryFieldImage() and passes the image data contained in the binary field. binaryFieldImage() is contained in the class CGIImages (in the file HTMLImages.cc). Its function is twofold. The image data is (1) stored in a cache table (named dBLTImageCache.dbf, which is create by the program) and (2) assigned a unique identifier. This identifier is the methodís return value. It is is stored in a memory variable named cImage along with the name of the CGI program that will retrieve the image for the browser (e.g. dbltimages.exe?95838892824).

Line 14 then inserts the content of cImage into an HTML image tag and streams that tag to the browser.

Line 15 streams a horizontal line.

Line 16 moves the row pointer and terminates the loop.

Line 17 stream the HTML page footer and terminates the program.

A complete HTML page has now been streamed to the web browser. The following is an example of such a page:
 
 
<HTML>
<HEAD>
 <META HTTP-EQUIV="Content-Type" CONTENT="text/html">
 <TITLE> HTML page with images</TITLE>
</HEAD>

<P>Item ID: 1
<P><IMG SRC="dbltimages.exe?47039252830" ALT="">
<HR>
<P>Item ID: 2
<P><IMG SRC="dbltimages.exe?79906609021" ALT="">
<HR>
<P>Item ID: 11
<P><IMG SRC="dbltimages.exe?44577164140" ALT="">
<HR>
<P>Item ID: 12
<P><IMG SRC="dbltimages.exe?72145512931" ALT="">
<HR>
</BODY>
</HTML>

   

The Web browser will receive this page and begin rendering line by line. When it reaches an <IMG> tag it will send a request to the Web server to run dbltimages.exe with the corresponding parameter. The Web server fulfills the request and passes the parameter to Ken Chanís dBL program. That program finds the correct row in the image cache DBF table, and the image data is returned to the browser with the proper HTTP headers.

Using dbltImages with the Web Wizards

There are at least two ways to stream a report from a web application. The first is to write code that loops through your table(s) and streams the data within an HTML table object. I personally use this technique for most of my web reports. It requires an elaboration of the TestApp.prg program that was reviewed above and gives me a high degree of control over the reportís layout.

The second way to produce a web report is to use a dBASE report object and set the output property to CGI Response. Many dBASE developers prefer to use dBASE reports and the Web Wizards, so it will be useful to review their relationship with the dbltImages system.

There are three steps that need to be followed in order to use dbltImages with the Web Wizards.

Step one

The Web Wizard approach requires that the images stored in your table get copied to the image cache before the report is run. In addition you must store a reference to the cached image along with the original image.

In the example files that accompany this article, the Inventory.dbf table includes a field named CacheID, which we will use to store the imageís key value. In your own application you will need to modify your table by adding a similar field.

Now we can run some code that will build the Image Cache and assign key values. The following program (taken from UpdateCache.prg) will do what we need. This is not a web application, even though it uses some of the web objects. It can be run from the command window.
 
 
Set proc to htmlImages.cc additive 
oCGI = new CGIImages() 

////// Open Query or Data Module
cTable = "INVENTORY.DBF" 
q = new query() 
q.sql = [Select * from "] + cTable + ["] 
q.active = true 
q.rowset.indexName = "Item ID" 
q.rowset.first()

do
   cImage = oCGI.binaryFieldImage( q.rowset.fields['Picture'].value )
   ? cImage
   q.rowset.fields['cacheID'].value = substr(cImage,16)
   q.rowset.save()
until not q.rowset.next()

   

When this program is finished running, the image cache will contain the images and an key value for each record. Moreover, the Inventory table, which is the table that is going to be used in the report, will also contain the image key.

Step two

Now we need to create a dBASE report. Inventory.rep is an example that we can work with in this article. The report contains a text field for the item description and another text field for the cacheID. We do not, however, want to stream the cacheID value to the browser. The user has little interest in this value. Instead, we need the text object to stream an image tag with the appropriate cacheID value inserted into the SRC property.

To do this you can use the text objectís canRender event. The event handler should look something like the following:
 
 
Function TEXTCACHEID1_canRender
   this.text = "<img src='dbltImages.exe?" + ;
   trim(this.form.inventory1.rowset.fields["cacheid"].value) + "'>"
   return true
   

When this object renders, it will be a single image tag and the SRC property will point to the dbltImages CGI applet.
 
 
<img src='dbltimages.exe?47039252810'>
   

Of course, this tag will not render in the Report designer, and therefore, you will not see the image or the plain text (the dBASE Text object knows this is an HTML tag). The text object will appear empty in the Report designer, but the tag will be rendered when the report is streamed.

If you want to test your report, you can add some bootstrap code that sends the output to an HTML file. You can then open that file and inspect the output as the web browser will receive it. Hereís an example of the bootstrap code:
 
 
local r
r = new InventoryReport()
r.output = 4
r.outputFilename = "TestReport.htm"
r.render()
return
   

Add this at the very top of the report file (line one), run the file, and then open TestReport.htm in the Source Code Editor to see how the report is converted from a dBASE object to an HTML page.

Step three

When the report is working the way you want it, you can run the dBASE Web Wizards. This will create the CGI application for running the report.

When you deploy this report to your web server, be sure that you also deploy the image cache files (dbltImageCashe.dbf, dbltImageCache.dbt, and dbltImageCache.mdx). Also be sure that you deploy the updated table used by your report. If the table on the web server does not contain the correct image key values, your report will not work as designed.

Conclusion

Ken Chan has given us a very valuable tool for delivering images from a binary field to a web browser. The technique can be used in your CGI program that streams data stored in a table to a Web browser. The technique can also be used with a dBASE report that is rendered from a program built by the Web Wizards.


The author would like to thank Ken Chan for sharing his dBL Template code and Dwight Purdy, his proofreader, for the improvements he brought to this text.