Joseph Hachey

blog

  Home  |   Contact  |   Syndication    |   Login
  8 Posts | 1 Stories | 8 Comments | 25 Trackbacks

News



Article Categories

Archives

Post Categories

It's called a starter kit, so it's safe to assume most people that use the Personal Website Starter Kit are not professional web developers. It's probably also safe to assume that most student or hobbyist developers who would use the kit do not admin their own server rack, and must rely on purchased hosting plans to publish their personal websites to the Interweb. So then why would Microsoft create a starter kit that stored all of the website's pictures (times 4 different sizes) in a SQL server instance?? Do they realize how much hosted database space costs?!? With my hosting plan, I receive a whoppin' 20GB of webspace along side a puny 200MB of SQL Server space. It's easy to see my motivation to modify the website starter kit so that it stores its pictures on the filesystem instead of the database.

Disclaimer: I am fresh out of college, and by no means an expert. With that said, I offer no warranties on the modifications. Like most others, I am using the starter kit as a learning tool. If you notice any errors in my code or conventions, please feel free to post a comment or send an email so that I may correct the error and learn from my mistakes. But please no nitpicking. Now on with the show...

All updated files involved in the modification can be downloaded here.

After completing this modification, your website will store all pictures aqcuired via the manage albums/photos pages on the server's filesystem at '~/Images/Albums/'. The PhotoManager will not only create the image files on the server when a picture is uploaded, but delete the files as well when albums/pictures are removed via the manage albums/pictures pages. Because the web app handles all image file related tasks, you should not manual add/delete files in '~/Images/Albums' or risk throwing the database and associated files out of sync.

The database schema must be altered to store file locations instead of the actual pictures. Below is a diagram depicting the new schema:

After changing the schema, be sure to alter the stored procedures, 'GetFirstPhoto' and 'GetPhoto', so that they return the location column instead of the actual picture.

The Microsoft dev that put the starter kit together definately had the concept of abstraction figured out, thus making my life a whole lot easier. Besides alterations to a few stored procedures and adding another property to the Photo class, all modifications were contained to the PhotoManager class. Listed below are the PhotoManager methods that were either added or modified, along with the alterations on the stored procedure:

Photo.cs

public class Photo

{

private int _id;

private int _albumid;

private string _caption;

private string _location;

public int PhotoID { get { return _id; } }

public int AlbumID { get { return _albumid; } }

public string Caption { get { return _caption; } }

public string Location { get { return _location; } }

public Photo(int id, int albumid, string caption, string location)

{

_id = id;

_albumid = albumid;

_caption = caption;

_location = location;

}

}


PhotoManager.cs

public static Stream GetPhoto(int photoid, PhotoSize size)

{

using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Personal"].ConnectionString))

{

using (SqlCommand command = new SqlCommand("GetPhoto", connection))

{

command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add(new SqlParameter("@PhotoID", photoid));

bool filter = !(HttpContext.Current.User.IsInRole("Friends") || HttpContext.Current.User.IsInRole("Administrators"));

command.Parameters.Add(new SqlParameter("@IsPublic", filter));

connection.Open();

string result = (string) command.ExecuteScalar();

try

{

//-Get image location on server

string path = HttpContext.Current.Server.MapPath("~/Images/");

path += result;

Stream pic = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);

return ResizeImageFile(pic, size);

}

catch

{

return null;

}

}

}

}


public static Stream GetFirstPhoto(int albumid, PhotoSize size)

{

using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Personal"].ConnectionString))

{

using (SqlCommand command = new SqlCommand("GetFirstPhoto", connection))

{

command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add(new SqlParameter("@AlbumID", albumid));

bool filter = !(HttpContext.Current.User.IsInRole("Friends") || HttpContext.Current.User.IsInRole("Administrators"));

command.Parameters.Add(new SqlParameter("@IsPublic", filter));

connection.Open();

string result = (string) command.ExecuteScalar();

try

{

//-Get image location on server

string path = HttpContext.Current.Server.MapPath("~/Images/");

path += result;

Stream pic = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);

return ResizeImageFile(pic, size);

}

catch

{

return null;

}

}

}

}


public static List<Photo> GetPhotos(int AlbumID)

{

using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Personal"].ConnectionString))

{

using (SqlCommand command = new SqlCommand("GetPhotos", connection))

{

command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add(new SqlParameter("@AlbumID", AlbumID));

bool filter = !(HttpContext.Current.User.IsInRole("Friends") || HttpContext.Current.User.IsInRole("Administrators"));

command.Parameters.Add(new SqlParameter("@IsPublic", filter));

connection.Open();

List<Photo> list = new List<Photo>();

using (SqlDataReader reader = command.ExecuteReader())

{

while (reader.Read())

{

Photo temp = new Photo(

(int)reader["PhotoID"],

(int)reader["AlbumID"],

(string)reader["Caption"],

(string)reader["Location"]);

list.Add(temp);

}

}

return list;

}

}

}


public static void AddPhoto(int AlbumID, string Caption, byte[] BytesOriginal)

{

int photoID;

string location;

//-Add photo to DB

using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Personal"].ConnectionString))

{

using (SqlCommand command = new SqlCommand("AddPhoto", connection))

{

command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add(new SqlParameter("@AlbumID", AlbumID));

command.Parameters.Add(new SqlParameter("@Caption", Caption));

connection.Open();

using (SqlDataReader reader = command.ExecuteReader())

{

reader.Read();

photoID = reader.GetInt32(0); //- in case photo has to removed from DB later

location = reader.GetString(1);

}

}

}

//-Save photo to filesystem

string path = System.Web.HttpContext.Current.Server.MapPath("~/Images/");

path += location;

try

{

WriteToFile(path, ref BytesOriginal);

}

catch

{

//-If picture fails to save on filesystem, remove picture's record from DB

using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Personal"].ConnectionString))

{

using (SqlCommand command = new SqlCommand("RemovePhoto", connection))

{

command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add(new SqlParameter("@PhotoID", photoID));

connection.Open();

command.ExecuteNonQuery();

}

}

}

}


public static void RemovePhoto(int PhotoID)

{

string location;

//-Remove photo from DB

using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Personal"].ConnectionString))

{

using (SqlCommand command = new SqlCommand("RemovePhoto", connection))

{

command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add(new SqlParameter("@PhotoID", PhotoID));

connection.Open();

location = (string) command.ExecuteScalar();

}

}

//-Remove photo from filesystem

string path = HttpContext.Current.Server.MapPath("~/Images/");

path += location;

if (File.Exists(path))

File.Delete(path);

}


public static void RemoveAlbum(int AlbumID)

{

string location;

using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Personal"].ConnectionString))

{

using (SqlCommand command = new SqlCommand("RemoveAlbum", connection))

{

command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add(new SqlParameter("@AlbumID", AlbumID));

connection.Open();

location = (string) command.ExecuteScalar();

}

}

string path = HttpContext.Current.Server.MapPath("~/Images/");

path += location;

if (Directory.Exists(path))

Directory.Delete(path, true);

}


private static Stream ResizeImageFile(Stream imageFile, PhotoSize targetSize)

{

if (targetSize == PhotoSize.Original)

{

//-Retun pic without resizing

return imageFile;

}

else

{

//-Convert pic to byte[]

byte[] picBuff = StreamToBytes(imageFile);

//-Resize pic

if (targetSize == PhotoSize.Large)

{

picBuff = ResizeImageFile(picBuff, 600);

}

else if (targetSize == PhotoSize.Medium)

{

picBuff = ResizeImageFile(picBuff, 198);

}

else if (targetSize == PhotoSize.Small)

{

picBuff = ResizeImageFile(picBuff, 100);

}

//-Convert buffer back to stream and return

return new MemoryStream((byte[])picBuff);

}

}


private static byte[] StreamToBytes(Stream stream)

{

//-Create buffer

int byteCount = (int)stream.Length;

byte[] buffer = new byte[byteCount];

//-Read stream into buffer

using (stream)

{

stream.Seek(0, SeekOrigin.Begin);

stream.Read(buffer, 0, byteCount);

}

//-Return buffer

return buffer;

}


private static void WriteToFile(string path, ref byte[] buffer)

{

//-Ensure directory is present

string dir = Path.GetDirectoryName(path);

if (!Directory.Exists(dir))

Directory.CreateDirectory(dir);

//-Create picture file

using (FileStream newFile = new FileStream(path, FileMode.Create))

{

newFile.Write(buffer, 0, buffer.Length);

}

}


Stored Procedures:

ALTER PROCEDURE AddPhoto

@AlbumID int,

@Caption nvarchar(50)

AS

INSERT INTO [Photos] (

[AlbumID],

[Caption],

[Location])

VALUES (

@AlbumID,

@Caption,

'tmpLocation') /* tmpLocation needed because app broke when Location column set to Allow NULLs */

/* Retrieve generated PhotoID */

DECLARE @PhotoID int

SET @PhotoID = SCOPE_IDENTITY()

/* Build unique location path from album and photo ID */

DECLARE @Location nvarchar(50)

SET @Location = 'Albums\' + CONVERT(nvarchar(10), @AlbumID) + '\' + CONVERT(nvarchar(10),@PhotoID) + '.jpg'

/* Update photo with new location path */

UPDATE [Photos]

SET

[Location] = @Location

WHERE

[PhotoID] = @PhotoID

/* Return PhotoID and Location */

SELECT @PhotoID, @Location

RETURN


ALTER PROCEDURE RemovePhoto

@PhotoID int

AS

/* Retrive photo's location */

DECLARE @Location nvarchar(50)

SET @Location = (

SELECT [Location]

FROM [Photos]

WHERE [PhotoID] = @PhotoID)

/* Delete photo record */

DELETE FROM [Photos]

WHERE [PhotoID] = @PhotoID

/* Return deleted photo's file location */

SELECT @Location

RETURN


ALTER PROCEDURE RemoveAlbum

@AlbumID int

AS

DELETE FROM [Albums] WHERE [AlbumID] = @AlbumID

/* Build deleted album's directory location */

DECLARE @Location nvarchar(50)

SET @Location = 'Albums\' + CONVERT(nvarchar(10), @AlbumID) + '\'

/* Return deleted album's directory location */

SELECT @Location

RETURN

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati
posted on Wednesday, December 07, 2005 8:21 PM

Feedback

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 12/8/2005 11:59 AM Kerry Kilgour
good work on this...I may need to look into that soon for the same reason you state above...

one good habit I recommend is using the Path.Combine (System.IO) to join files and pathnames. then you don't have to remember if your path had a (back)slash on the end or not.

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 12/27/2005 11:55 PM Jon C
Thanks for your work on this, just saved me a bunch of time in a moment of need. Thanks

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 2/7/2006 7:09 AM Bill
Anyone got a working VB version of this?

The ResizeImageFile method is giving me grief with conversions from streams to bytes... i don't know how your code could work.. when it recursively calls itself it passes back a byte datatype rather than a stream... but hey i'm tired and have been staring it for too long!

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 4/12/2006 1:56 PM Gary Park
Similar to the above post, I am getting errors with the ResizeImageFile Method recursizely calling itself and receiving the error message cannot convert from byte to stream.

Am I missing something!!

Thanks for taking the time to do the above by the way, have been wanting to do it for a while, and now would just like to get this last part sorted out!

Cheers

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 4/12/2006 2:38 PM Gary Park
Please ignore my last post, just downloaded the source files from your link at the top of the page and realised that it is not a new ResizeImageFile Method, but rather an overloaded version of it!!!

Thanks again for the code, much appreciated!!!

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 4/12/2006 2:43 PM Ian Houghton
try this website to code conversion
http://www.ragingsmurf.com/vbcsharpconverter.aspx

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 4/14/2006 2:38 PM Carly
Anyone got this to work with the Club Starter kit?

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 5/18/2006 9:27 AM Atanas Kumbarov
Hi,
this is really great but I still can't make it run. Can somebody describe the process in steps? Something like "Database to file system for dummies". I cannot find the files that should be modified in my project. I also didn't get what should I do with the database.

And it will also be great if there was an option to store the user's info in some file not in the database. What I mean is completely wipe the database.

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 5/19/2006 3:53 PM Rob
Wonderful write-up, is there anyway you can go in more detail about the changes to the database and also get a VB version

Thanks

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 5/21/2006 9:58 PM Nestor Sulu
Great! I was in need of same change, I started one version of myself then I found your version, I simply ended using yours. Nice code, thanks, you save me a lot of time.

# File system save for Club Site 6/4/2006 5:22 PM ivelander
Has anyone gotten this to work with the Club Starter Kit? I would assume it would be a fairly similar process but am a bit wary to start ripping the code apart. Thanks!

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 8/7/2006 11:52 AM jonny
where do i put the images now and is there a bulk upload still

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 3/28/2007 5:41 PM pete
Hi can the source files for this project still be downloaded.
Cheers

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 12/8/2007 9:19 AM Houssein
i want to download te file but it gives me wrong URL can anybody help me?

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 2/5/2008 4:31 PM Stan
Can anyone repost the file? The link is bad.

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 7/20/2008 9:38 PM Priya Ranjan Hota
I want to have my own website.

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 8/19/2008 9:15 PM manhtuyen
tôi không thể xóa được file ảnh trong ổ đĩa bằng chương trình asp.net, thư mục chứa ảnh là một thư mục nằm ngoài thư mục chứa project của chương trình
có ai biết cách nào để xóa nó không
thanks!


# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 10/16/2008 11:05 PM Radu
i have a question about the starter kit administration, if i put it on a server(purchased hosting ) will i be able to upload photos and text from a remote location ? I really need help on this one

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 8/1/2009 8:14 AM tiffany
I think this article is very helpful!Thanks downloaded the newsletter and forwarded it to my friends.

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 11/30/2010 1:34 AM anu
thank you sir... its really good...

# re: ASP.NET 2.0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database 8/20/2011 1:43 AM Athul
Please add a new link to download the modified files.

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: