This article provides a brief overview of the architecture and design of FlashBack, a personal web based catalog and picture sharing web site.
FlashBack was built using the .NET framework, ASP.NET and C#. I will try to share my experiences and highlight some architectural and design choices I had to make, while developing it. I hope that you will find FlashBack useful and adapt it for your own needs. Several versions of FlashBack have been running successfully for about 2 years.
To keep this article relatively short I will cover mostly the architecture and high level design. I will try to provide brief implementation details for some of the core components. If you are interested you can browse the source code and get the details of the implementation.
The latest version of FlashBack (including user documentation) is available from the FlashBack Workspace.
You can see also a limited online demo version. This demo version is limited to not show picture upload functionality and the administration interface.
(Please note that the demo version runs on port 8008. If you can't connect, make sure your firewall/proxy allows outgoing connections to port 8008)
Following are the key features:
- Provides access to pictures and video clips, albums and album structure using a web browser.
- Filter albums based on date posted.
- Preview images in albums as thumbnails or view each image individually in a slide show. To conserve bandwidth, lower resolution thumbnails and slide images can be generated automatically from your original images.
- Plays video clips using an embedded player (Windows Media player or Quick Time).
- Download and upload images/clips to albums. Create and manage albums from a web browser.
- Search for images based on attributes such as keywords and date created/posted. Search uses Microsoft Indexing Service for background indexing of picture attributes.
- Administrators can restrict access to albums based on user groups. FlashBack uses an internal user/group structure that is independent from Windows users and groups.
- E-mail notifications can be sent to selected users notifying them when new pictures are available.
Targeted for Internet Explorer, but a limited version can be used with any browser. There is also some limited support for small screen browsers such as Pocket Internet Explorer used in Windows CE/ Pocket PC operating systems.
FlashBack has built-in web based administration tool for:
- Managing users, groups and access to folders
- Monitoring user activity
- Monitoring application health
The following diagram provides an overview of the application architecture. Each block represents a component that typically resides in a separate assembly. Dotted lines present some of the component dependencies. The block arrows show the general direction of data flow between the components.
As you can see, the architecture of the application is centered in a large part around the presentation tier (blue components). For this application, the presentation layer accounts for most of the functionality. Business logic is relatively simple and straightforward.
Most of the presentation is delivered through ASP.NET web forms, C# "code behind," and a couple of custom user controls. Most of the business logic is delivered by application logic components (purple and yellow). Application components typically interface with some external entity – file system, indexing engine or the event log. Yellow components wrap COM interfaces. Purple components are responsible for various internal file, image and security management functions.
Note that there is no back end database. All required information is stored in the file system. This makes it very easy to move, setup, backup and restore the entire picture collection.
The main user scenario for this application is browsing albums and viewing pictures. The workflow in this scenario is relatively simple:
- When a user clicks on an album in the tree view (left pane, as shown in Figure 1), the request is received by an ASPX page and displayed in the main (right) pane. The path to the album is provided as a query string parameter. The
OnLoad method of the page dispatches the request to the
DirectoryHelper class in the
ApplicationTools component requesting a list of images in this album. This class is responsible for:
- Retrieving pictures or video clips in this album.
- Formatting the pictures to the requested resolution.
- Filtering out pictures the current user is not authorized to see.
- The returned list of images (an
FileSystemInfo object) holds all pictures of a particular format that the user is authorized to see. Based on what view the user has requested (thumbnails, slide show), the list contains files with different resolution (size). All required image formats and sizes are created automatically from the original pictures and cached for later use.
- After converting this
ArrayList to a
DataTable, the list is used to data bind to a
DataGrid control for display. In several places during the data binding process, the physical path to the picture is replaced with a virtual path needed to correctly render the image in a web browser
This general workflow is repeated with some variations for all the three major views: Thumbnails, Slide Show and Video Clip.
A web-based image catalog must manage several different image formats (resolutions/sizes). The primary format is the image’s original format. On average, a 2 megabit digital camera uses about 300 KB per image. It would be very inefficient to send a dozen or more 300KB images just for displaying as thumbnails. To make this process more efficient, FlashBack generates lower resolution thumbnails (80x100 pixels, about 5 K per image) from the original pictures automatically, as you visit each album. Thumbnails are generated once and cached to a directory structure that mimics the directory structure of the originals. The cached directory structure is hidden and maintained internally by the application. For all practical purposes, you only have to worry about the originals.
The same process is repeated for slide images (images that are displayed in the slide show view). Generated slide images are about 800x600 pixels or approximately 50K per image. If you have fast connection (and a large monitor) you can tell FlashBack to use the original images as slide images.
As mentioned above, the user interface tier uses the services of the
ApplicationTools component to get the correct list of pictures to display. The
DirectoryHelper class presents an API that makes the file system look like a hierarchical repository of pictures. It abstracts the user interface from knowing much about where the pictures are stored. The central method of
DirectoryHelper class is the
GetPictureList method. This method returns a list of pictures from a given album with the correct format (size).
GetPictureList method checks if the requested album exists and if the requested format (thumbnails or slideshow) is available. If the selected format does not exist, it is generated on the fly and cached. The actual work of generating the pictures is done by the
ThumbnailGenerator class, located in the
If you delete some original pictures after they have been cached, the
GetPictureList will try to detect that and update its cache accordingly. It uses a picture count in the original and cached albums to detect this change, so if you just replace a picture it will not detect it.
One potential issue with on-the-fly generation is that, if given album has a large number of pictures, your user will have to wait until all images are generated before the application returns a result. This also ties up an IIS worker thread for the duration of the request, thus hindering scalability. Fortunately this happens only once - on the first request for an album - and is acceptable for "non-commercial" environments. In a commercial environment, this could be done by a background service or as a part of a manual deployment step after uploading new pictures.
Thumbnail generator class
ThumbnailGenerator is responsible for generating different image sizes from an original image. Internally it uses the built- in
System.Drawing.Bitmap class to manipulate the image - load, resize and save.
The use of
ThumbnailGenerator class is very simple. You construct the class by providing a path to the original image. The
Generate method accepts size and the name of a file where the generated image will be saved. The supplied size is the size of the largest dimension of the new image. The other dimension is calculated based on the picture’s aspect ratio. This ensures that the new picture will be no larger than the requested size in both dimensions.
ApplyBevel method allows you to apply a bevel effect on the edges of the generated image. The code for this effect comes from an article published on this site by Philipos Sakellaropoulos. (In this article Philipos also provides some sample code of how to extract thumbnail images from other file types such as Word documents or movie clips.)
DirectoryHelper class, as part of executing the
GetPictureList method, is also responsible for filtering pictures that the requesting user is not authorized to see. This filtering is done by presenting each picture to the
SecurityManager and asking it if the user is allowed access.
I kept the design of the security subsystem as generic as possible to make it reusable in other applications. At the base level, the security subsystem controls access to virtual resources. In the case of Flashback, the resource is a directory. In other projects there may be other types of resources to protect.
The FlashBack security subsystem works at group/album level granularity. You can control access to particular albums, but not to individual pictures in the album. When albums are nested, denying access to the parent also denies access to all child albums.
User authentication is done by the UI tier using a built-in forms authentication mechanism. At this time the
SecurityManager is asked to validate the user credentials. The validated user is stored in a session variable and available for later requests.
All credential information that the
SecurityManager uses is stored in in-memory structures. It is serialized to an XML file when changes are made and desterilized when the application starts.
Security checks are performed using an expression of type:
In addition to the authentication and authorization interfaces, the
SecurityManager provides an interface to manage users and groups as well as to manage access to resources.
The only permission that FlashBack currently uses is the
Read permission. You cannot control which users have rights to upload or delete pictures to which albums. FlashBack allows the administrator to define a “public area” in the album tree, where users are allowed to upload and delete pictures. The administrator can only determine which users can upload pictures. All users who are allowed to upload pictures have the same access to the “public” area. The private or restricted area is managed by copying pictures directly to the file system on the server.
Integration with Microsoft Indexing Service
FlashBack can interface with Microsoft Indexing Service to search for pictures based on some picture properties. Currently only default Windows 2000 properties are searchable: Title, Subject, Author, Categories, Keywords and Comments. (More properties can be searched, if you implement a plug-in to the Indexing Service called
IFilter). You can set any of the properties mentioned above by right-clicking on the picture and selecting Properties and then selecting the Summary tab.
FlashBack uses the
IndexService component to interface with the indexing service. There are two COM objects provided by Microsoft and installed by default with the indexing service. These objects talk to the indexing service, accepting search criteria and returning back a list of files that satisfy these criteria - in a way similar to querying a database. FlashBack’s
IXQuery class presents a managed API that wraps around both COM components. It also provides some transformation of the result, so it comes back as an
ArrayList of original pictures. Based on the search criteria, this list may contain pictures from multiple albums. The result list is further provided to an overload of
GetPictureList method, that ensures that the appropriate picture formats are generated and security rules applied. Finally, the filtered and formatted list is used as a data source for data binding to Web form controls in the user interface.
Logging and monitoring component
Any application needs a way to log events that occurred during its execution. In FlashBack the
Support component provides tracing and logging services to the rest of the application. Most of the code comes from a reference implementation (Microsoft Duwamish sample).
ApplicationLog is the main interface for logging events. Every time a component detects a significant event (usually errors, but other conditions like creating a user or group are also considered important), it logs this event using some of the
WriteTrace. By default, the event is logged to two places – an application specific event log and to a text log file.
Each event is also sent to the tracing infrastructure that is built in the .NET framework. This provides a standard mechanism for intercepting events and sending notifications.
NotificationTraceListener class in the
ApplicationTools assembly intercepts events, matching a certain pattern (a regular expression), and sends E-mail messages to the administrator’s E-mail account. By default only user login events and error events are being monitored, but this is configurable.
Some time ago Microsoft released the exception handling application block that provides similar functionality. There is also an open source project called Log4Net that provides application logging.
Good logging is fine, but does not help much if you don’t review your logs at regular intervals. FlashBack uses a proactive notification as described above, and also allows the administrator to view the application event log as part of his administration screens.
Logging can also be used to track site usage. Given that the application knows the time the user logged in and the IP address of the user, it is relatively easy to query web server log files and find all files accessed by a given IP within a time period. In the administration screen, there is a link that allows you to query for all accessed files in a given user session.
IISHelper component contains the
IISLogBrowser class that interfaces with web server log files. Microsoft provides a COM component that makes it easy to query log files as if they are a database table.
IISLogBrowser relies internally on this COM class for its implementation.
In addition to the automatic event notification described above, FlashBack supports sending of template E-mail messages to selected groups of users. Two sample template E-mails are included: –
ForgottenPassword, for when the user requests their forgotten password and
NewPictures, – which the administrator can send to notify users when new pictures are added to the site.
E-mail templates are defined by a text file that stores the subject and body of the E-mail. You can place some user specific information in the E-mail by specifying named tokens. These tokens will be replaced with the correct user information for each user, when E-mails are generated. Currently supported tokens include
The administration screens provide a way to select users and add additional data to the message.
FlashBack uses the
MailHelper class from the
ApplicationTools component to perform token transformation and send the E-mails. The
MailHelper internally relies on the built-in
System.Web.Mail.SmtpMail class and a thread pool to send E-mail messages asynchronously.
The goal when designing the user interface was to make it as simple as possible, but still be able to accommodate growing additional functionality. It evolved several times and reflected my experience in previous projects. I decided to give it more of an application feel rather than a traditional web page feel. Use of frames and
IFRAMEs made Flashback less portable, but satisfied my requirements for an easy and direct way to interact with the application.
Bands control class
The left pane was implemented using a custom “bands” control. This control is used to switch between Albums/Search/Administration modes. The number of bands and their names are customizable. Each band hosts an
IFRAME that points to a content file. The album's band hosts the tree control.
Tree control class
The tree control is a server side user control. This control is fairly customizable so you can change how each node is rendered. This control uses a
TreeControlModel class that abstracts the tree structure (what is in the tree) from the presentation logic (how does the tree look like). The tree control renders fairly well on different browsers – it was tested on Netscape, Pocket IE and IE. I have successfully reused this control in other projects.
The Thumbnails view consists of a
DataGrid control that renders thumbnails in a
DataGrid and supports paging. You can control how many rows and columns are displayed at a time.
The Movie Clip view hosts an embedded player with a list of video clips in the album. This view will render either Windows Media Player or Quick Time player based on the contents of the album. What is in the album is determined by looking at the extension of the first file in the album – if it is .MOV – FlashBack assumes that the album contains Quick Time movies and renders the Quick Time player. Otherwise it renders the Windows Media player.
I tried to make this application configurable, so it can be fit in many environments. Web.config is the primary configuration file. For details, see the comments in the configuration file distributed with the application. FlashBack also supports side by side execution of different versions, or as in my case, several instances of the same version hosting different image catalogs.
This application has evolved incrementally over a period of time. Pieces were added as needed - usually as a result of feedback or ideas I had. Several times I had to re-factor existing code and make it easier to understand and maintain. There are places where code looked "good-enough" or was not worth touching.
This was also my playground to try different technologies and approaches. This may explain why some features seem like overkill for this type of application. I attempted to put my best architecture, design and implementation skills at the time. I am sure that if I have had to build it again, I would do many things differently.
If you decide to use this application for more than just personal use, keep in mind that in several cases I resorted to the easiest way of doing something, rather than the “right” way (picture upload for example). There are also some security issues allowing you to bypass the security infrastructure and download any picture if you know the full URL. Implementing a custom
HttpModule can resolve this issue. I also mentioned before that scalability is also hindered by the way low resolution images are generated.
There is also a suite of unit tests (using
NUnit) that test various application components. Some cleanup is needed before they can be uploaded to the workspace.
In its current state FlashBack is stable and to a certain degree a maintainable and extendable application. It does exactly what it supposed to do and I have successfully used it for a while. I hope you can find some use for it too.