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 posted in the CodeLib
group. Hereís the reference:
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.
Ken Chanís system replaces the URL pointing to
the image with a URL pointing to a dBL CGI application. Something like
There are two processes that make this system work.
Among the sample files that accompany this article,
you will find a dBASE table named Inventory.dbf
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()
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")
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="">] )
16 until not q.rowset.next()
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:
<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
<TITLE> HTML page with images</TITLE>
<P>Item ID: 1
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.
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
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.
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:
this.text = "<img src='dbltImages.exe?" + ;
trim(this.form.inventory1.rowset.fields["cacheid"].value) + "'>"
When this object renders, it will be a single image
tag and the SRC property
will point to the dbltImages CGI
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:
r = new InventoryReport()
r.output = 4
r.outputFilename = "TestReport.htm"
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.
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.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.
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.