Sie sind auf Seite 1von 10

ASP.NET 2.

0 Personal Website Starter Kit: Storing Pictures on Filesystem Instead of Database


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
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
ti khng th xa c file nh trong a bng chng trnh asp.net, th mc cha nh l mt th mc nm ngoi th mc cha project ca chng trnh c ai bit cch no xa n khng 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.

Das könnte Ihnen auch gefallen