In the first part of my GearsZipper tutorial I showed you how to create zip archives with in Gears with the help of JSZip. This already worked fine, but our only possible content were Javascript strings. What if we want to add “real”, external files to our zip? If we take the use case of our CSS Editor, this could be static graphic assets like PNGs for border or shadow effects. We certainly don’t want the user to download these seperately.

Extension #1: Adding support for “real” files

Luckily, Gears helps us again. The already mentioned RessourceStore class also has a captureBlobmethod, that allows you to convert an arbitrary url to a Blob. A Blob represents the binary content of a file, thus we can use its getBytes method to convert the file content to a format that JSZip understands. getBytes returns the file content as an array of integers, with Javascript’s built-in String.fromCharCode method we can convert this integer array to a byte string that JSZip understands. Voila, now we are able to include “real” files.

However, this feature also introduced a little drawback: since the captureBlob is asynchronous, we also need to make our utility function asynchronous, but this shouldn’t make our code too complicated. Here is a demo of showing the embedding of real files.

Photo uploads and their problems

A high percentage of all uploading activity is related to photos. Online photo galleries, photo sharing sites or even blog offer specialized upload features for photos. There are quite a few problems related to photo uploading:

  1. Resize before upload Unexperienced users often want to upload the original photos from their camera without resizing them before. This leads to enormous upload times and/or failed uploads.
  2. Upload of multiple photos Thanks to the openFiles() method of Gears’ Desktop class, its quite easy to implement multi-file uploads on the client side. The headaches start on the server side: the uploaded photos arrive peu-a-peu at the server, you need to send session id’s with each photo to assign them to a certain user. Additionally, an increment counter (e.g. “photo 5 of 10″) must be transmitted. Furthermore its also more complicated to notify the user of the upload progress.

There are many Java Applets that target this use case, but if you want minimal dependencies, then its better to stick with Gears. As I already mentioned, Gears is like a time machine that brings the future features of HTML 5 to the browsers from nowaday. Even its API is modelled close to the HTML 5 spec, so once your browser supports HTML 5 natively, it will only cost you little effort to port your uploading solution from Gears to HTML 5. At the time of this writing, Firefox 3.6, the first browser with a complete HTML 5 implementation is ante portas and the others will soon follow. Thus, sooner or later you have a fully standardized solution without any external dependencies.

Batch Upload of resized photos in a zip file

We try to tackle the above scenario with a simplistic approach: all selected photos are resized and added to one zip archive, which can then be processed at once on the server side. Of course this approach has drawbacks, but it saves us from coding a complex server backend and might be applicable to some uploading use cases. In order to create the zip archive, each selected photo has to undergo the following process:

  1. Determine the width and height of the selected photoTo maintain the aspect ratio of the image, I first determine the width and height of the original image by calling the desktop.extractMetaData() method. It takes a Blob as parameter and gives you all the metainformation it knows about it. For images, it returns height and width.
  2. Resize each selected photo to a maximum width of x pixels. This is possible with the Gears Canvas API. Contrary to HTML 5’s Canvas element, the Gears Canvas is an offscreen image processor. It doesn’t draw anything on your screen, but offers resize and crop manipulations for existing images. In my demo, I wanted to resize all images to a maximum size of 400 pixels, so I calculated the new proportional height with the formula Math.round(400/(metadata.imageWidth/metadata.imageHeight))
  3. Encode the resized image We want to add the contents of the resized file to our zip, so we call the Canvas.encode() method. Encoding here actually means that the raw pixels of a canvas image are written to a specific file format. With the mimeType and options parameters of Canvas.encode() you specify the file format (JPEG or PNG) and the compression rate. Canvas.encode() returns a blob with the contents of the file, which we can pass on to GearsZipper.
  4. Adding the blob to the zip Now we can feed the generated blob with a filename to GearsZipper. Internally, GearsZipper encodes the blob to a byte string and feeds it to JSZip. The result is a .zip archive containing resized versions of all selected images.

Further optimisations (exercise to the reader)

If you tried the batch upload demo with a few photos, you might have experienced an unpleasant behaviour: your browser freezed for minutes and the famous Abort this script? message appeared. The reason: the computationally expensive processing of the zip file happened in the main “thread” of the browser that is also responsible for drawing the UI and responding to the user input. It would be highly desireable if the zip creating happened in the background and Gears’ WorkerPool API is the perfect way to achieve this. I haven’t found time to implement this, so I leave it as a nice exercise to the reader…

Conclusions

I hope this series showed you the true potential of Gears and also some interesting applications of client-side zipping. At first sight these use cases might not be obvious, but some client-centered applications could be coded much easier if also the “data export” part resided on the client side. Apps like Themeroller, Picnik and others would certainly benefit from such an approach. Furthermore, the advent of HTML 5 and its file api could also break the last barrier for bringing many desktop apps to the web.