SubjectThis tutorial discusses the use of the Whitebeam 'Binary' class, how
to use the class to read and manipulate file data, transfer data between browser,
templates and the file system. The following topics are covered:
OverviewThe Whitebeam System provides a special JavaScript class called Binary.
This class is used to encapsulate 'foreign' data. The object contains data in
any format - the size of the data only being limited by available memory. The
data itself can be anything and is treated by the system as an opaque (i.e.
has no meaning to the system) sequence of octets. Binary objects are commonly used to contain the contents of a file - either
a real one or the contents of a file pushed from a client side application (e.g.
a web browser). For most applications the Presentation Page doesn't need to directly
access the data within the object. It is often simply carried either from the
file system to the client, the client to a template or a template to the client.
The data itself is never actually inspected. There are two ways that a Binary object can be created. The simplest - but
least common - is by using the new JavaScript operation. The other is for
them to be created by other operations within the system. For example a template
method can be defined to return a Binary object. When the method is invoked
it will return an instance of the Binary class. The Binary
Object class defines a number of methods that can be used to manipulate
binary objects and their contents. The Binary object can be used for many applications. The following is not
meant to be a complete list - and is for information only. Receiving 'Files' From Users.The HTTP protocol allows clients to send files to the application using the
'POST' method. The end-user selects a file from the file system and the client
application sends the name, mime-type and contents of that file to the
web server. Because files can contain arbitrary data the client must know to use
a special 'encoding' in order to send the data to the application. This is
known as a 'multi-part mime' encoding. You tell the client to use this encoding
using the 'enctype' attribute on the <form.../> HTML tag. For
example - suppose an application allows a user to upload a picture (eg GIF) file
of themselves as part of a user profile. The HTML form tag might look like:
<form action="profile.rhtm" method="post" enctype="multipart/form-data">
...
</form>
The important parts of this tag are the method (must be "POST") and the enctype,
(must be "multipart/form-data"). In the Whitebeam environment the application gets the contents of that
file using the rb.page.formdata().
This method creates an object with a property for each parameter send from the client
browser. Where that parameter is a file the method creates an instance of the
Binary object. If you don't set the method and enctype as described above the
files - if loaded at all will appear as simple JavaScript strings. Sending 'Files' To UsersThis is the opposite operation from that described above. In this case there exists
some file on the server that needs to be sent to the client from a Presentation Page. The
most common example of this a Presentation Page that provides authenticated access to things
like images. The following Presentation Page fragment will load an file from the file system
and send it to the browser: var filecontents = new Binary();
filecontents.load("picture.gif"); rb.page.setMimeType("image/gif"); rb.page.writeraw(filecontents);
Storing Data in the File TemplateThe File template stores
arbitrary data in a conceptual file. The primary intention of this template
is to store things like 'GIF' images of product items. In practice it can store
any set of data - up to a maximum of 120Kbytes. A template 'file' therefore
provides an excellent place to store sets of application data in a consistent
and persistent way. It is also possible to store Metadata in the file template using exactly the
same model as for storing Metadata in templates (such as Catalogue). However
the benefit in using the file template is that it allows much larger objects
to be stored (up to about 120Kbytes). The Presentation Page can store the data in
the object using the bin.fromObj() method, store the resulting
binary object in the File template using rb.file.create()
method and subsequently recover that data using the bin.toObj()
method. Creating a 'File' in a BinaryIn content management applications you may wish to dynamically create HTML
or XML text and save it in the File template for subsequent display. This is
quite easy to accomplish. Assuming that the variable docText is a string that
holds the dynamically created document: var filecontents = new Binary();
filecontents.set(docText);
filecontents.mime("text/html"); // Give it a mime type and
filecontents.fname("generated.xml"); // a name
and then doing an rb.file.create() will save the dynamically created
file using the File template. So - for example - to store the above file in the
database you might use the following code:
// Get the uploaded file from the form data.
var params = rb.page.formdata();
// Find the file from the name we gave the <input file
// element in the form tag.
var theFile = params.uploadFile;
// Now create a database entry for this file.
var fileDescr = new Object;
fileDescr.name = 'Fred Bloggs Profile Picture';
fileDescr.owner = 'Application';
fileDescr.description = '';
fileDescr.fileDef = theFile
// Finally - call the template to store the image in the database for us.
var fileId = rb.file.create(fileDescr);
// The method returns the ID of the newly created file object. This
// needs to be stored so we can find the file again later! Generally
// this ID would be stored in a catalogue item or a contact record -
// in the meta data for those objects.
Supposing we'd stored the returned ID in the contact record for this user we can send it back to the client
browser using something like the following code.
// The user ID is a parameter called 'userId' passed from the browser - get it
var params = rb.page.formdata();
var userId = params.userId;
if (userId!=null) {
// Load the contact record.
var usr = rb.contact.individualInfo(userId);
if (usr==null)
rb.debug.error('Invalid userId from client');
// Have the user - get the fileID.
var fileId = usr.customData.profileImage;
if (fileId>0) {
// Load the file from the database.
var fileIterator = rb.file.get({fileID:fileId});
if (fileIterator.getNextRow()) {
// The file is in fileIterator.fileDef - a Binary object.
// Assuming this is a display page - write it to the user
// using the 'writeraw' method.
rb.page.setMimeType(fileIterator.fileDef.mime());
rb.page.sendHTTPheaders();
rb.page.writeraw(fileIterator.fileDef);
}
}
}
The above example illustrates the entire cycle of uploading a file from browser - storing that in the file
template and then writing it back to a client. Obviously any data can be stored in the file object itself.
Common examples are data created by the application itself, including complex, serialised JavaScript objects, audit logs,
reports etc that can subsequently be retrieved.
Mime Types and Arbitrary, non-HTML outputThe example above also illustrates how to write arbitrary data to a client in such a way that the
client can correctly deal with it. File contents are nominally identified to the client via a 'mime-type'. By
default the mime-type is set to 'text/html' - so if you want to write some other kind of data you must
first set the correct mime type for your application. This could be either an image (image/gif) or just
plain text (text/plain) that you don't want the browser to treat as HTML. Generally having set the mime-type to something other than HTML, you will want to send your data (of course!). By
default the Presentation Engine is constructing output as it executes the page. While it is perfectly possible to
write the Presentation Page generating only the output you want. a much easier way is to call the
rb.page.writeraw() method. This bypasses
all the XML/HTML processing of the Presentation Engine and writes data directly to the client. Note however that if
you use this you should call
rb.page.sendHTTPheaders()
first. If you don't then you are limited to one call the rb.page.writeraw (because the browser will be told
how much data to receive in the first chunk and not wait for any more). Once you'v called rb.page.sendHTTPheaders you can call rb.page.writeraw as many times as you like. Each
call results in data being sent directly to the client. This is a very useful technique if you have a page that takes
a very long time to execute, such as a long report - you can construct a page that is continually updated. Advantages of Binary Objects and the File templateThe Whitebeam environment is designed to be very highly scaleable - to allow the capacity of the
data center to be extended simply by the addition of a new front-end processor (FEP). In order to achieve
the scaleability in the most efficient way all the FEPs must be identical. In turn, to achieve this, the FEPs
must be stateless. Using the database to store files achieves this - one FEP can store a file in the database
and the next moment a separate FEP can retrieve this data. |