Ok, it's been a bit of time since I posted some technical content. I seem to be the only codebetter blogger who has big business aspirations and is launching a real ISV so I've been trying to mix that content into the Codebetter feed for those of you who have similar aspirations. I think it's time to get back to some technical content though.
Using SQL Server to manage images (or other files) is a frequently asked question of mine. I have come across several clients in my consulting career where they were simply dumping the files into a physical directory on the webserver. In the end, this is pretty sloppy and difficult to manage especially if for some reason you want to reorganize the data.
In this article I will show you the Easy Assets .NET method of handling image uploads. The application allows clients to store images of their assets, mostly for insurance purposes. I had a few goals for this section of the application as follows:
Store images in sql server for easy management on my end.
Automatically compress images so I don't have to worry about disk space usage or demand that my users compress it themselves which can be hard for a user without computer savvy (once again, EASY assets .net)
Generate thumbnails for display in a list.
Step 1: Configuring SQL Server
First thing we must do is set up a SQL Server table to handle the data. SQL Server has a handy image field to handle this type of thing. Here's the table we'll be using in the example:
CREATE TABLE [dbo].[AssetImages] ( [AssetImageID] [int] IDENTITY (1, 1) NOT NULL , [AssetID] [int] NOT NULL , [FileData] [image] NOT NULL , [FileName] [nvarchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [FileSize] [bigint] NOT NULL , [ContentType] [nvarchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [Thumbnail] [image] NOT NULL , [LastEditUser] [nvarchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL , [LastEditDateTime] [datetime] NOT NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO
The main image will be stored in the FileData field and the thumbnail image will be stored in the Thumbnail field.
Step 2: Compressing the image
To compress the image, I used the handy tool I mentioned in this post. You simply register the dll, and drag and drop the compressor tool onto the page. The following code uploads an image from the html file upload object, compresses it, and then generates a thumbnail. It uses my domain pattern, like all pages in Easy Assets .NET.
Private Sub btnUpload_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUpload.Click
Try
Dim upfile As HttpPostedFile = UploadFile.PostedFile
Dim thumbnail As Bitmap = CreateThumbNail(New Bitmap(upfile.InputStream, False), 120, 120)
thumbnail.Save(stream, ImageFormat.Jpeg)
assetImage.Thumbnail = stream.GetBuffer()
DomainManager.Save(assetImage)
BindDataGrid()
End If
Catch ex As Exception
WriteMessage(ex.Message, True)
End Try
End Sub
Few things to note:
Filesize / 1000... default is bytes, I convert it to kilobytes.
Bitmaps and memory streams are pretty cool, I know that the bitmap has a thumbnail creation method on it but I've heard that it doesn't do a nice job. I found the following code on a MVP's site, sorry to whoever it was, I cleared my history and lost your blog. Contact me and I'll give you proper credit.
Private Function CreateThumbNail(ByVal postedFile As Bitmap, ByVal width As Integer, ByVal height As Integer) As Bitmap
Dim bmpOut As System.Drawing.Bitmap
Dim Format As ImageFormat = postedFile.RawFormat
Dim Ratio As Decimal
Dim NewWidth As Integer
Dim NewHeight As Integer
'*** If the image is smaller than a thumbnail just return it
If postedFile.Width < width AndAlso postedFile.Height < height Then
Return postedFile
End If
If (postedFile.Width > postedFile.Height) Then
Ratio = Convert.ToDecimal(width / postedFile.Width)
NewWidth = width
Dim Temp As Decimal = postedFile.Height * Ratio
NewHeight = Convert.ToInt32(Temp)
Else
Ratio = Convert.ToDecimal(height / postedFile.Height)
Now, by default, you can't really show image data from sql server into a datagrid, so we have to sort of "trick" ASP .NET into inserting an image into the grid. We do this by calling a special aspx page into the template column of the grid. Here's how it works:
First, set up the template column of the grid as such:
Notice the call to the two pages, on click of the thumbnail I want to show them the full-size version of the photo in a popup window. The thumbnail is bound to a function called "formaturl" that takes the id of the image as a parameter. Let's take a look at this function:
Protected Function FormatURL(ByVal imageID As Integer) As String
So basically we have told asp.net that the source of this image is the results of the AssetShowThumb.aspx. But what does this page do? Let's take a look:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
If Not Page.IsPostBack Then
Dim assetImg As New EasyAssets.DAC.AssetImage(Convert.ToInt32(Request.QueryString("ID")))
So all we've done is load the image record, set the content type to jpeg (I force all saves in the above function to be jpegs), and binary write the image data. We end up with a display like this:
And an original image (on click of thumb) like this:
And oh how I wish this picture, from my hawaii trip, was actually an asset of mine. =)