I built a piece of code sometime ago and today I decided to optimize it.
My use case is this: a user can upload some photos of his house for example. Image dimensions (height x width) are not restricted. The user can upload any photo as long as its size is less than or equal to 750 KB.
Then, I have a listing page (Index.cshtml) that has a WebGrid. The grid shows realties available for sell, rent, etc. In this grid I want to display just a thumbnail of a given photo (the first one I grab as per code bellow) for each realty. I placed a constraint such that the thumbnail dimensions must be 100x100 pixels. Of course one shouldn’t load the full size version of a photo just to show it on the grid. The grid has 10 rows per page and if the images are on the range of 500 KB, the load put on the user connection to transmit all those photos from the server to the client would be high: 10 x 500 KB ≃ 5 MB. This would be an unresponsive page! No doubt…
Googling a little bit I stumbled upon something I didn’t know yet. It’s called WebImage and comes in System.Web.Helpers just as the WebGrid I discussed earlier. With this little kid on the block, one can have a controller “action” that does something like this:
public void GetPhotoThumbnail(int realtyId) {
// Loading photos’ info from database for specific Realty...
var photos = DocumentSession.Query<File>().Where(f => f.RealtyId == realtyId); if (photos.Any()) { var photo = photos.First(); new WebImage(photo.Path) .Resize(101, 101, false, true) // Resizing the image to 100x100 px on the fly... .Crop(1, 1) // Cropping it to remove 1px border at top and left sides (bug in WebImage) .Write(); } // Loading a default photo for realties that don't have a Photo new WebImage(HostingEnvironment.MapPath(@"~/Content/images/no-photo100x100.png")).Write(); }
Calling the .Write() method does the magic. As you can see, I’m not saving the thumbnail in the disk, that is, a new image is generated on the fly/dynamically and sent to the user’s browser. Amazing…
In a view using the Razor syntax one would have something like this to display the photo:
<img src="@Url.Action("GetPhotoThumbnail", new { realtyId = item.Id })" width="100" height="100"/>
Another useful variation/overload of the above action method is this one (even more dynamic):
public void GetPhotoThumbnail(int realtyId, int width, int height) { // Loading photos’ info from database for specific Realty... var photos = DocumentSession.Query<File>().Where(f => f.RealtyId == realtyId); if (photos.Any()) { var photo = photos.First(); new WebImage(photo.Path) .Resize(width, height, false, true) // Resizing the image to 100x100 px on the fly... .Crop(1, 1) // Cropping it to remove 1px border at top and left sides (bug in WebImage) .Write(); } // Loading a default photo for realties that don't have a Photo new WebImage(HostingEnvironment.MapPath(@"~/Content/images/no-photo100x100.png")).Write(); }
Then, in the view one would call the action passing the width and height variables as per requirement:
<img src="@Url.Action("GetPhotoThumbnail", new { realtyId = item.Id, width = 100, height = 100 })" />
There are a handful of available functions in a WebImage. You should really check them out.
You can find more insightful info about the WebImage class reading this post by Gunnar Peipman: ASP.NET MVC 3 Beta: Simple image manipulations using WebImage helper
Note: there really are better ways of handling this kind of situation (adding image caching, etc – see ImageResizer) but for the site I’m working on, the solution I show in this post is sufficient and pretty responsive right now.
Hope it helps.