Build Your Own Photo Gallery: Part I

Getting Started

Bipin Joshi

October 30, 2009

15 Min Read
ITPro Today logo

CodeTalk

LANGUAGES:C#

ASP.NETVERSIONS: 2.0

 

Build Your Own Photo Gallery: Part I

Getting Started

 

By Bipin Joshi

 

You probably preserve many moments of your life in photos.And you probably want to share those moments with your friends and relatives. Aphoto gallery is the perfect place to share your snapshots with others. This three-partseries will demonstrate how you can build a photo gallery complete withcategories, thumbnails, and security using ASP.NET 2.0 and SQL Server 2005.In this first part we ll start with database design and a few utility classes.

 

Functional Requirements

Let s first summarize our expectations for the photogallery application we are going to build:

  • The administrator should be able to createalbums in the system.

  • The administrator should be able to mark thealbums as public or private.

  • The administrator should be able to uploadphotos to the albums.

  • The photos should be stored in a SQL Serverdatabase and not as physical files.

  • The administrator should be able to specify attributesfor the photos, such as title and description.

  • The photo thumbnails should be generatedautomatically as per the specified dimensions without any need to upload smallimages.

  • The full photo is to be displayed when a userclicks on the thumbnail.

 

ASP.NET Feature Illustration

We will use ASP.NET 2.0 and SQL Server 2005 ExpressEdition to develop the photo gallery. The application will illustrate:

  • How to store and retrieve images in a SQL Serverdatabase.

  • How to display images in data bound controlssuch as DataList and FormView.

  • How to create thumbnail images on the fly.

  • How to work with a file upload control to uploadfiles on the server.

  • How to implement security using Formsauthentication, Membership, Roles, and login controls.

 

Creating the Web Application

To begin, create a new ASP.NET Web site using VisualStudio .NET or Visual Web Developer. Figure 1 shows the New Web Site dialog boxof Visual Studio .NET.

 


Figure 1: Creating a new Web site inVS.NET.

 

All the administrative pages are stored in a folder namedAdmin; other pages are stored directly under the virtual root. Create a folder namedAdmin inside the application folder.

 

Database Schema

All the data, including photos and album definitions, willbe stored in a SQL Server 2005 database. To add a new SQL Server database inyour Web site, right click on the App_Data folder and select Add New Item toopen the dialog box shown in Figure 2.

 


Figure 2: The Add New Item dialogbox.

 

Name the database PhotoDb.mdf. Once the database iscreated we can create tables in it. Figure 3 lists all the tables and theircolumns that we need. Figure 4 shows the database diagram of these two tables.

 

Table Name

Columns

Description

Albums

AlbumID (int, Identity)

Name (varchar)

Description (varchar)

IsPublic (byte)

DateCreated (DateTime)

This table stores the photo album definitions.

Photos

PhotoID (int, Identity)

AlbumID (int)

Name (varchar)

Description (varchar)

Image (Image)

ThumbnailHeight (int)

ThumbnailWidth (int)

DateCreated (DateTime)

This table stores photos and related information.

Figure 3: Createthese tables and columns in PhotoDb.mdf.

 


Figure 4: Database diagram of theAlbums and Photos tables.

 

Handling Data Access

To perform our database operations we use the SQL DataSource control. Data source controls act as a bridge between your database andthe user interface. They help reduce the amount of code you write for dataaccess. The SQL Data Source control also allows you to develop two-tierapplications. Though most of the data access is taken care via the SQL DataSource control, at times we need to execute queries on our own. For thatpurpose, we ll create a class named SqlHelper that encapsulates such commontasks as opening a connection, closing a connection, and executing the queries.

 

To create the SqlHelper class, right click on yourApp_Code folder and select Add New Item. Then select Class from the Add NewItem dialog box and name it SqlHelper. Figure 5 lists the main methods of the SqlHelperclass.

 

Method

Description

public static int ExecuteNonQuery(string query)

Allows you to execute action queries without any parameters.

public static int ExecuteNonQuery(string query, SqlParameter[] p)

Allows you to execute action queries with parameters.

public static SqlDataReader ExecuteReader(string sql)

Allows you to execute SELECT queries without any parameters and get returned results in a SqlDataReader.

public static SqlDataReader ExecuteReader(string sql, SqlParameter[] p)

Allows you to execute SELECT queries with parameters and get returned results in a SqlDataReader.

public static object ExecuteScalar(string sql)

Allows you to execute queries without any parameters that return a single value.

public static object ExecuteScalar(string sql, SqlParameter[] p)

Allows you to execute queries with parameters that return a single value.

public static DataSet ExecuteDataSet(string sql)

Allows you to execute SELECT queries without any parameters and get returned results in a DataSet.

public static DataSet ExecuteDataSet(string sql, SqlParameter[] p)

Allows you to execute SELECT queries with parameters and get returned results in a DataSet.

Figure 5: Methodsof the SqlHelper class.

 

As you can see, all the methods of SqlHelper are static sothat you can call them without creating an instance of SqlHelper. Listing One shows the complete source code of the SqlHelperclass.

 

We store the database connection string in the web.configfile. Figure 6 shows the relevant markup of web.config.

 

 providerName="System.Data.SqlClient"/> Figure 6: The databaseconnection string in the web.config file.   As you can see, we use the section to store the connection string. Note the connection string format. SQLServer 2005 Express edition allows you to dynamically attach database files.The .mdf file to attach is specified via the AttachDbFileName attribute. Becausewe use the default authentication mode of SQL Server 2005 Express, we set theIntegrated Security attribute to True.   In the static constructor of the SqlHelper class, we pickup the above connection string. In each of the public methods, we essentiallycreate a new instance of the SqlConnection and SqlCommand objects. We thenconfigure the command object by setting its properties, such as CommandText andConnection. If our query has any parameters, we also populate the Parameterscollection of the command object using a private method (FillParameters). Wethen call the ExecuteNonQuery, ExecuteScalar, or ExecuteReader methods of thecommand object.  Web Forms and Master Pages Create the Web forms and master pages, as shown in Figure 7.   Web Form Name Folder Description AdminMasterPage.master Admin This is the master page for administrative Web forms. AlbumManager.aspx Admin Manages albums; i.e., add, edit, and delete album definitions. PhotoManager.aspx Admin Manages photos. Allows you to upload photos and categorize them in albums. MasterPage.master Root Acts as a master page for the album and photo pages. Default.aspx Root Acts as the home page of the photo gallery and displays a list of all albums in the system. DisplayAlbum.aspx Root Displays thumbnails of all the images from an album, along with their names and descriptions. DisplayPhoto.aspx Root Displays a selected photo, along with its name and description. GetPhoto.aspx Root Gets a photo or its thumbnail from the SQL Server database. Login.aspx Root Users are required to log in to access private albums. This form acts as a log-in page for private albums and administrative tasks. Figure 7: List of Webforms used in the application.  Creating the PhotoHelper Utility Class Any photo gallery application needs to frequently dealwith images and their thumbnails. We ll create a utility class namedPhotoHelper that allows us to retrieve photos and thumbnails from the databaseand display them in Web forms. To create this class, right click on theApp_Code folder and choose Add New Item. Name the class PhotoHelper. Figure 8lists the methods of this class.   Method Description public static Image GetThumbnail(int photoid) This method accepts a photo ID and returns its thumbnail image as an Image. public static Image GetThumbnailForAlbum(int albumid) This method accepts an album ID and returns the thumbnail of the latest photo from that album as an Image. public static Image GetPhoto(int photoid) This method accepts a photo ID and returns the whole photo image in the form of an Image. Figure 8: Methodsof the PhotoHelper class.   Note that you need to import System.Drawing,System.Drawing.Imaging, and System.IO before you proceed with the coding ofthis class:  using System.Drawing; using System.Drawing.Imaging; using System.IO;   Let s examine all the methods of PhotoHelper; Figure 9lists the complete source code for the GetThumbnail utility method.  public static Image GetThumbnail(int photoid) {string sql = "SELECT IMAGE,THUMBNAILHEIGHT,THUMBNAILWIDTH FROM PHOTOS WHEREPHOTOID=@id"; SqlParameter[] p = new SqlParameter[1]; p[0] = new SqlParameter("@id", photoid); SqlDataReader reader = SqlHelper.ExecuteReader(sql, p); byte[] imagedata = null; int height = 0, width = 0; while (reader.Read()){imagedata = (byte[])reader.GetValue(0); height = reader.GetInt32(1); width = reader.GetInt32(2); }reader.Close();MemoryStream ms = new MemoryStream(imagedata); Image bigImage = Image.FromStream(ms); Image smallImage = bigImage.GetThumbnailImage(width,  height, null,IntPtr.Zero); return smallImage; }Figure 9: The GetThumbnailmethod.   The GetThumbnail method accepts the photo ID whosethumbnail is to be created. Then it fetches the actual image and its thumbnailheight and width from the database using the ExecuteReader method of the SqlHelperclass. The image data is read into a byte array called imagedata. This image isfurther converted into a MemoryStream. This is necessary because later we needto create an Image from this stream. The Image class has a static method namedFromStream. This method accepts a stream and constructs an image out of it. Thereturn type of the FromStream method is another class named Image. The Imageclass further has a method named GetThumbnailImage that creates a thumbnail ofthe image. The parameters to the GetThumbnailImage method are: thumbWidth: Indicates the width of the thumbnail. thumbHeight: Indicates the height of thethumbnail. GetThumbnailImageAbort: Indicates a callbackmethod for determining when the GetThumbnailImage method should abort theexecution. callbackData: Is of type IntPtr and must be zero.   We finally return the thumbnail Image instance back to thecaller. The second method, GetThumbnailForAlbum, is shown in Figure 10.  public static Image GetThumbnailForAlbum(int albumid) {SqlParameter[] p = new SqlParameter[1]; p[0] = new SqlParameter("@albumid",albumid); int photoid = int.Parse(SqlHelper.ExecuteScalar("SELECT TOP 1 PHOTOID FROM PHOTOSWHERE ALBUMID=@albumid ORDER BY DATECREATED DESC",p).ToString());return GetThumbnail(photoid); }Figure 10: The GetThumbnailForAlbummethod.   Albums by themselves do not have a photo. However, itwould be nice to have a thumbnail for the album cover. That is precisely what theGetThumbnailForAlbum method does. It simply fetches the latest photo added tothe album and returns a thumbnail for it in the form of an Image.   Finally, Figure 11 lists the source code for the GetPhotomethod. The code in Figure 11 is similar to Figure 9 but does not create athumbnail. The GetPhoto method accepts the ID of the photo to be retrieved andreturns the complete image retrieved from the database.  public static Image GetPhoto(int photoid) {string sql = "SELECT IMAGE FROM PHOTOS WHEREPHOTOID=@id"; SqlParameter[] p = new SqlParameter[1]; p[0] = new SqlParameter("@id", photoid); SqlDataReader reader = SqlHelper.ExecuteReader(sql, p); byte[] imagedata = null; while (reader.Read()){imagedata = (byte[])reader.GetValue(0); }reader.Close();MemoryStream ms = new MemoryStream(imagedata); Image bigImage = Image.FromStream(ms); return bigImage; }Figure 11: The GetPhotomethod.  Creating Master Pages We need two master pages. One, AdminMasterPage.master, foradministrative pages, and the other, MasterPage.master, for the remainingpages. Figure 12 shows the general layout of AdminMasterPage.master.  
Figure 12: Layout ofAdminMasterPage.master.   To create this master page, add a new Master Page in the Adminfolder using the Add New Item dialog box (again, see Figure 1); name itAdminMasterPage.master. Figure 13 shows the markup of this master page.  <%@ Master Language="C#"AutoEventWireup="true" CodeFile= "AdminMasterPage.master.cs"Inherits="AdminMasterPage" %>

 "~/Images/logo.gif"NavigateUrl="~/Admin/Default.aspx">

 DynamicHorizontalOffset="2" Font-Names="Verdana" Font-Size="12px"ForeColor="#7C6F57"  StaticSubMenuIndent="10px" Width="100%">  Text="ManageAlbums" Value="Manage Albums">  Text="ManagePhotos" Value="Manage Photos">

 runat="server">


Figure 13: Markupof AdminMasterPage.master.

 

The master page has a HyperLink at the top. The ImageUrlproperty of the hyperlink is pointed to the logo of the Web site. To allow theadministrator to navigate through the administrative pages we use a menucontrol. To design the navigation menu, drag and drop a Menu control onto themaster page. Open the Menu Item Editor from the smart tags of the Menu controlby choosing Edit Menu Items (see Figure 14). Add two menu items and set theirproperties as shown in Figure 15.

 


Figure 14: The Menu Item Editor.

 

Menu Item For

Text

NavigateUrl

Managing album definitions

Manage Albums

~/admin/albummanager.aspx

Managing photos

Manage Photos

~/admin/photomanager.aspx

Figure 15:Configuring the menu.

 

Because AdminMasterPage.master is applied only toadministrative pages, we put code to ensure that only administrative users canaccess such pages. Figure 16 shows the Page_Load event handler ofAdminMasterPage.master.

 

protected void Page_Load(object sender, EventArgs e)

{

if (!Roles.IsUserInRole("ADMIN"))

{

throw new Exception("You are not allowed

 to access thispage!");

}

}

Figure 16:Page_Load of AdminMasterPage.master.

 

Here we check if the user belongs to a role called ADMINusing the IsUserInRole method of the Roles object. If the IsUserInRole methodreturns false, we throw an exception.

 

The MasterPage.master looks similar toAdminMasterPage.master, but does not contain the menu. Figure 17 shows themarkup of this Web form.

 

<%@ Master Language="C#"AutoEventWireup="true"

 CodeFile="MasterPage.master.cs"Inherits="MasterPage" %>

 ImageUrl="~/Images/logo.gif"NavigateUrl="~/Default.aspx">   [HyperLink1]

 runat="server">


Figure 17: Markupof MasterPage.master.

 

As before, the master page also has a HyperLink at thetop. The ImageUrl property of the hyperlink is pointed to the logo of the Website and the NavigateUrl property points to Default.aspx.

 

Conclusion

In the first of this three-part series we designed SQLServer database tables that will be used further to store photos. We alsodeveloped two utility classes, SqlHelper and PhotoHelper. The former will beused for all database interactions and the latter will be used for generatingthumbnails and retrieving photos. We also developed two master pages, one foradministrative pages and one for album pages. We also used role-based securityto ensure that only administrators can load administrative pages. In Part II we lldevelop the administrative pages for managing albums and photos.

 

The sample code forthis series is available for download.

 

Bipin Joshi is thefounder and owner of BinaryIntellect Consulting (http://www.binaryintellect.com),where he conducts professional training programs on .NET technologies. He isthe author of Developer s Guide to ASP.NET 2.0(http://www.binaryintellect.com/books)and co-author of three WROX books on .NET 1.x. He writes regularly for http://www.DotNetBips.com, a communityWeb site he founded in the early days of .NET. He is a Microsoft MVP, MCAD,MCT, and member of ASPInsiders. He jots down his thoughts about .NET, life, andYoga at http://www.bipinjoshi.com. Healso conducts workshops on Yoga and Meditation, where he helps IT professionalsdevelop a positive personality. You can contact him at mailto:[email protected].

 

Begin Listing One the SqlHelperclass

using System;

using System.Data;

using System.Data.SqlClient;

public class SqlHelper

{

 private static stringstrConn;

static SqlHelper()

{

strConn = ConfigurationManager.ConnectionStrings[

 "ConnectionString"].ConnectionString;

}

public static int ExecuteNonQuery(string query)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(query, cnn);

cnn.Open();

int retval = cmd.ExecuteNonQuery();

cnn.Close();

return retval;

}

public static int ExecuteNonQuery(string query,

 SqlParameter[] p)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(query, cnn);

FillParameters(cmd, p);

cnn.Open();

int retval = cmd.ExecuteNonQuery();

cnn.Close();

return retval;

}

public static SqlDataReader ExecuteReader(string sql)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

return cmd.ExecuteReader(CommandBehavior.CloseConnection);

}

public static SqlDataReader ExecuteReader(

 string sql, SqlParameter[]p)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

FillParameters(cmd, p);

cnn.Open();

return cmd.ExecuteReader(CommandBehavior.CloseConnection);

}

public static object ExecuteScalar(string sql)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

cnn.Open();

object retval = cmd.ExecuteScalar();

cnn.Close();

return retval;

}

public static object ExecuteScalar(string sql,

 SqlParameter[] p)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

FillParameters(cmd, p);

cnn.Open();

object retval = cmd.ExecuteScalar();

cnn.Close();

return retval;

}

public static DataSet ExecuteDataSet(string sql)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

SqlDataAdapter da = new SqlDataAdapter();

da.SelectCommand = cmd;

DataSet ds = new DataSet();

da.Fill(ds);

return ds;

}

public static DataSet ExecuteDataSet(string sql,

 SqlParameter[] p)

{

SqlConnection cnn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(sql, cnn);

FillParameters(cmd, p);

SqlDataAdapter da = new SqlDataAdapter();

da.SelectCommand = cmd;

DataSet ds = new DataSet();

da.Fill(ds);

return ds;

}

private static void FillParameters(SqlCommand cmd,

 SqlParameter[] parameters)

{

for (int i = 0; i < parameters.Length ; i++)

{

cmd.Parameters.Add(parameters[i]);

}

}

}

EndListing One

 

 

 

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like