Like it

Sunday, July 17, 2011

WebMatrix - Uploading Images with the FileUpload Helper

Microsoft WebMatrix Tutorial Series

Part 1 - Introduction to WebMatrix
Part 2 - Installation and Setup
Part 3 - Creating a Database with SQL CE
Part 4 - Retrieving Content from a Database
Part 5 - Adding and Editing Data
Part 6 - Working with Foreign Key Relationships
Part 7 - Uploading Images with the FileUpload Helper
Part 8 - Authentication and Authorization
Part 9 - Displaying a list of popular posts
Part 10 - Adding Comments to your blog with the DISQUS helper
Part 11 - Searching your site with Bing
Part 12 - Integrating your site with Social Media Helpers
Part 13 - Publishing your site

Overview

The final section in adding and editing blog posts is to add the functionality to upload a photo with each blog post.  To do this we will use the WebMatrix FileUpload helper.  The FileUpload helper was included as part of the WebMatrix installation in earlier beta versions, but was removed when WebMatrix was shipped and now needs to be installed manually.

Installing Helpers

A Helper is a reusable ASP.NET component that can be used in WebMatrix and ASP.NET MVC projects.  There are many built in helpers provided, and third party helpers from Microsoft partners are being constantly added.  Some of the popular helpers featured on http://www.microsoft.com/web/webmatrix/partners.aspx include:
eventprite Eventbrite
The Eventbrite Helper for WebMatrix makes it simple to promote your Eventbrite events in your WebMatrix site. With the helper in place you can also easily sell tickets, or leverage the Eventbrite API for managing your events information.
Facebook Facebook
The Facebook helper is designed to integrate your WebMatrix site with Facebook, making it possible to add the Facebook Social Plugins, such as Like button, Facepile, Comments, Login Button and Like Box, among others, in a few simple steps.
bit.ly bit.ly
The Url Shortener Helper for WebMatrix and ASP.NET Web Pages allows you to easily shorten a url from any URL Shortener Provider.
Foursquare Foursquare
The Foursquare Helper for WebMatrix makes it simple to integrate Foursquare in your site. With a few lines of code you'll be able to show an "Add to My Foursquare" button or show any user's badges in your site.
Helpers are installed through the Web Pages Administration console that can access just by going to the /_Admin/ folder in your project in a web browser.  When you run a WebMatrix project in a web browser while developing, the local version of IIS that gets installed with WebMatrix automatically assigns your site a port number.  In the example screenshot below, the port number is 38130, so the local development site is housed at http://localhost:38130/ .  To access the administration functions, simply go to http://localhost:38130/_Admin/.
If this is the first time you've accessed the administration console then you'll need to create a password:
image
Once you have created your password, verified it, and logged in, you will see the package manager:
image
Search for and install the latest version of the ASP.NET Web Helpers Library. This library includes common components such as Captcha validation, Twitter profile and search boxes, Bing Search, etc.
image
The package adds a number of new files to your project:
image

Uploading pictures when adding a blog post

To use the FileUpload helper, you call FileUpload.GetHtml() in your form.  The default rendering displays an option to upload multiple files with an Upload button enclosed by a
tag.
image
The GetHtml method supports the following parameters:
  • name - The value for the name attribute of the HTML input element that is rendered for this helper.
  • initialNumberOfFiles - The initial number of file upload fields to display. The default is 1.
  • allowMoreFilesToBeAdded - true to allow more file upload fields to be added by the user at run time; otherwise,false. The default is true.
  • includeFormTag - true to include a form element around all file-upload input elements and the around the submit button; otherwise, false. The default is true.
  • addText - The text to display as a link that lets users add file upload fields.
  • uploadText - The text to display on the submit button.
Because we already have a form and are limiting our users to a single picture then our GetHtml method has the following parameters:
<tr> 
<td
 Picture: 
</td><td
 @FileUpload.GetHtml(initialNumberOfFiles:1,allowMoreFilesToBeAdded:false,includeFormTag:false)
  </td></tr> 
Which now makes our form look like this:
image
Because we've set includeFormTag:false then the Upload button is not rendered and the picture only gets uploaded when we click our Save Post button.  This only works if you already have a tag in your form as follows:
<form action="" enctype="multipart/form-data" method="post">
If you leave out enctype="multipart/form-data" then you will get an error "Index was out of range. Must be non-negative and less than the size of the collection."
To save the picture into the database, we'll use a variable called Picture that is made up of the raw image bytes.  First we get the uploaded file, and if it exists then read in the data into an array of bytes.
var UploadedPicture = Request.Files[0];  
byte[] Picture;  
if(Path.GetFileName(UploadedPicture.FileName) != String.Empty) 
{   
var ContentType = UploadedPicture.ContentType;   
var ContentLength = UploadedPicture.ContentLength;    
var InputStream = UploadedPicture.InputStream;   
Picture =new byte[ContentLength];   
InputStream.Read(Picture,0, ContentLength); 
}
else   
Picture = new byte[0]; 
}
The full code block for AddPost.cshtml now becomes:
@using System.IO; 
@{    Layout = "~/_SiteLayout.cshtml"
var db = Database.Open("Blog"); 
var categories=db.Query("SELECT categoryid, name FROM Categories");         
if(IsPost)
var Title=Request["Title"]; 
var SubmittedBy=Request["SubmittedBy"]; 
var SubmittedOn=Request["SubmittedOn"];     
var Abstract=Request["Abstract"];     
var Content=Request["Content"];     
var CategoryID=Request["CategoryID"].AsInt(); 
var UploadedPicture = Request.Files[0]; 
byte[] Picture;     
if (Path.GetFileName(UploadedPicture.FileName) != String.Empty) 
 var ContentType = UploadedPicture.ContentType; 
 var ContentLength = UploadedPicture.ContentLength;       
 var InputStream = UploadedPicture.InputStream; 
 Picture = new byte[ContentLength]; 
 InputStream.Read(Picture, 0, ContentLength); 
else 
 Picture = new byte[0]; 
}     
var insertQueryString = "INSERT INTO POSTS " +     
"(Title, SubmittedBy, SubmittedOn, Abstract, Content, CategoryID,)"+     
" Picture) VALUES (@0, @1, @2, @3, @4, @5, @6)"
db.Execute(insertQueryString, Title, SubmittedBy, SubmittedOn, 
Abstract, Content, CategoryID, Picture); 
Response.Redirect("ManagePosts.cshtml"); 
}   
}

Displaying the image

To display an image saved in the database, we create a file called ShowImage.CSHTML with the following code:
@{   
if(Request["Id"].IsInt())
{     
var db = Database.Open("Blog"); 
var selectQueryString = 
"SELECT Postid, Picture from POSTS WHERE PostID = @0"
var UploadedPicture =
db.QuerySingle(selectQueryString, Request["Id"].AsInt()); 
Response.BinaryWrite((byte[])UploadedPicture.Picture); 
}   
}
This code gets the picture from the database corresponding to the PostId of the blog post.  The picture is displayed on our main page and on each post like this:
@{     
if (row.Picture != null
{       
 <img src="ShowImage.cshtml?ID=@row.PostID" /> 
}   
}
I've used the following css for styling the image nicely around the text:
.post img   
 padding: 20px 10px 10px 0;       
float:left;       
width:100px
}

which produces the following result:
image

Changing pictures when editing a blog post

Editing a blog post will use the same logic to add a new image, but we'll also give our administrators the option to keep or remove the existing picture.
image
The html markup is similar to AddPost.cshtml with the addition of a set of radio options to keep, remove, or replace the current picture.
<tr><td> 
Picture: 
 </td><td> 
<input type="radio" name="PictureChoice" value="Current" checked/>
Keep Current Picture 
<br /> 
<input type="radio" name="PictureChoice" value="Remove" /> 
 Remove Picture<br /> 
<input type="radio" name="PictureChoice" value="Change" /> 
 Change Picture<br /> 
 @FileUpload.GetHtml(initialNumberOfFiles:1,allowMoreFilesToBeAdded:false,includeFormTag:false
</td></tr> 
When the form is submitted, it will check what option the user has selected.  If keeping the current picture, we force the new picture to System.DBNull.Value if no picture exits – otherwise you will get an error when trying to pass the parameter to the database.
@{Layout = "~/_SiteLayout.cshtml";     
int id = Request["id"].AsInt(); 
var db = Database.Open("Blog"); 
var selectQueryString = "SELECT Title, Content, SubmittedBy, "
 " SubmittedOn, Abstract, Name, Picture from POSTS INNER JOIN Categories " +       
"on Posts.CategoryID = Categories.CategoryID WHERE PostID = @0"
var Post = db.QuerySingle(selectQueryString, id);     
var Title=Post.Title;     
var Abstract=Post.Abstract; 
var Content=Post.Content; 
var Category=Post.Name;     
var SubmittedBy=Post.SubmittedBy; 
var SubmittedOn=Post.SubmittedOn.ToShortDateString(); 
var categories=db.Query("SELECT categoryid, name FROM Categories"); 
var Picture=Post.Picture;    
if(IsPost)
switch (Request["PictureChoice"]) 
 case "Current"
 if (Picture==null
   Picture =  System.DBNull.Value; 
 break
 case "Remove"
 Picture =  System.DBNull.Value; 
 break
 case "Change"
 var UploadedPicture = Request.Files[0];         
 byte[] NewPicture;         
 if (Path.GetFileName(UploadedPicture.FileName) != String.Empty) 
  var ContentType = UploadedPicture.ContentType;           
  var ContentLength = UploadedPicture.ContentLength; 
  var InputStream = UploadedPicture.InputStream; 
  NewPicture = new byte[ContentLength]; 
  InputStream.Read(NewPicture, 0, ContentLength); 
  Picture = NewPicture; 
 }         
 else         
  Picture =  System.DBNull.Value; 
 break
Title=Request["Title"]; 
SubmittedBy=Request["SubmittedBy"]; 
SubmittedOn=Request["SubmittedOn"]; 
Abstract=Request.Unvalidated("Abstract"); 
Content=Request.Unvalidated("Content"); 
var CategoryID=Request["CategoryID"].AsInt(); 
var executeQueryString="UPDATE POSTS Set Title=@0, SubmittedBy=@1,"
 " SubmittedOn=@2, CategoryID=@3, Abstract=@4, Content=@5, Picture=@6 "
 "WHERE PostId=@7"
db.Execute(executeQueryString, Title, SubmittedBy, SubmittedOn,CategoryID, Abstract, Content, Picture, id);
Response.Redirect("ManagePosts.cshtml"); 
}
That's it for all of the editing functionality, now we need to secure our admin pages.

No comments:

Post a Comment