Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Resizing and Storing Image Files - File System and Database Part 1

0.00/5 (No votes)
21 Oct 2014 1  
This article will describe and demonstrate a technique for resizing and storing image files on the local file system and image meta data in SQL Server using ASP.NET 4.51.

Background

On several occasions I have been tasked with creating a web application that would allow the user to upload and store files image files (.jpg, .png, .gif, etc.) along with a corresponding text description.  The first time was a problem because I have never done this before.  Googling "store image files database file system" reveals a plethora techniques and opinions both pro and con on storing image data in databases or the file system.  However, this paper, To BLOB or Not To BLOB: Large Object Storage in a Database or a Filesystem summarizes "... objects smaller than 256K are best stored in a database while objects larger than 1M are best stored in the filesystem.".   A similar search on resizing images on the fly also returned a variety of programming techniques.  The resulting code presented here is the result of this research.

This article will not address the pro's and con's of either of these approaches to image file storage and resizing.  We will just make it happen starting with storing images on the file system while storing meta data pertaining to the image in a SQL Server database.  I will follow up with an additional article that will describe storing images in a database at a later date.

Using the code

The web project uses the .NET 4.51 framework so be sure you have it installed on your system.  Download and unzip the Visual Studio solution to a folder of your choice, and open it with Visual Studio 2012 or better.

The default aspx page uses the standard FileUpload, TextBox, Button, and GridView controls.  The GridView columns have been converted to templates and the GridView is bound to a SqlDataSource control.  The SqlDataSource control is in turn bound to the localDB found in the App_Data folder  It contains a single table where the file name, file path, and text description of the image is stored.  For simplicity, this application only accepts graphic files that are of MIME type "image/jpeg".  Other types such as PNG and GIF can be processed with minor changes in the code.  This project was developed and tested using Internet Explorer 11.

Run the code in Internet Explorer and choose a JPG image to upload.  Clicking the 'Submit Photo' button starts the process.  The FileUpload control receives the file during the resulting postback.  The button's click event handler checks for the existance of a file.  If none exists, or if not a JPEG mime type, no data is processed.  Otherwise control is passed to the ResizeAndSaveImage method.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.IO;

namespace StoringImages
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Button1_Click(object sender, EventArgs e)
        {
            if (FileUpload1.HasFile == false || FileUpload1.PostedFile.ContentType.ToLower() != "image/jpeg")
            {
                return;
            }

            ResizeAndSaveImage();

            SqlDataSource1.InsertParameters["FilePath"].DefaultValue = Server.MapPath("SavedImages");
            SqlDataSource1.InsertParameters["FileName"].DefaultValue = System.IO.Path.GetFileName(FileUpload1.PostedFile.FileName);
            SqlDataSource1.InsertParameters["Description"].DefaultValue = TextBox1.Text;
            SqlDataSource1.Insert();
        }

The first thing that must be done is to get the uploaded image into a Bitmap object and extract vital information:

       // Assumes JPG/JPEG images only
        protected void ResizeAndSaveImage()
        {
            using (Bitmap uploadedBmp = new Bitmap(FileUpload1.FileContent))
            {
                decimal origHeight = uploadedBmp.Height;
                decimal origWidth = uploadedBmp.Width;
                int newHeight = 500;
                int newWidth = Convert.ToInt32(newHeight / (origHeight / origWidth));

Here the original image height and width are saved and the height of the new, resized image is determined.  In this case the new height will be 500 pixels.  It can be any value, even larger than the original.  The new width is calculated so that the resized image doesn't appear squashed or stretched.  Next a Graphics object is created.  This object will process the image data.  We set several rendering properties to produce a high quality image:

                using (Graphics resizedGr = Graphics.FromImage(uploadedBmp))
                {
                    // Optional. These properties are set for the best possible quality
                    resizedGr.CompositingMode = CompositingMode.SourceCopy;
                    resizedGr.CompositingQuality = CompositingQuality.HighQuality;
                    resizedGr.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    resizedGr.SmoothingMode = SmoothingMode.HighQuality;
                    resizedGr.PixelOffsetMode = PixelOffsetMode.HighQuality;

At this point a new Bitmap is instantiated with the new, smaller height and width properties:

                    using (Bitmap resizedBmp = new Bitmap(uploadedBmp, newWidth, newHeight))
                    {   

The Graphics object is commanded to draw the new image into the new smaller Bitmap.  A MemoryStream object is created and the new Bitmap then saves the file to memory with the proper MIME type encoding.  The memory file is converted to a byte array that is in turn saved to disk:

                        resizedGr.DrawImage(resizedBmp, 0, 0);

                        using (MemoryStream resizedMs = new MemoryStream())
                        {
                            System.Drawing.Imaging.EncoderParameters encParms = new System.Drawing.Imaging.EncoderParameters(1);
                            
                            // This allows jpeg compression to be set to 90
                            encParms.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)90);
                            
                            resizedBmp.Save(resizedMs, GetImgCodecInf("image/jpeg"), encParms);
                            long msLen = resizedMs.Length;
                            byte[] resizedData = new byte[msLen];
                            resizedData = resizedMs.ToArray();

                            using (System.IO.FileStream fStream = new System.IO.FileStream(Server.MapPath("SavedImages") + @"\" + Path.GetFileName(FileUpload1.PostedFile.FileName), System.IO.FileMode.Create))
                            {
                                fStream.Write(resizedData, 0, resizedData.Length);
                            }

The image now has been resized and stored on disk.  The next step is to create yet another smaller corresponding thumbnail image that can be used as a preview in the GridView control.  This step is essentially the same as the first image processing routine accept the height of the finished image is 100 pixels.  Also the image quality parameters have been left out and to appease those who prefer not to use the C# using statement, the FileStream and Graphics object are closed and disposed of programmatically:

                            // Repeat process to create a thumbnail image, reusing resizedBmp
                            // This approach does not use the 'using' statement or the
                            // high quality graphics properties

                            origHeight = resizedBmp.Height;
                            origWidth = resizedBmp.Width;
                            int thumbHeight = 100;
                            int thumbWidth = Convert.ToInt32(thumbHeight / (origHeight / origWidth));

                            Bitmap thumbBmp = new Bitmap(resizedBmp, thumbWidth, thumbHeight);
                            Graphics thumbGr = Graphics.FromImage(thumbBmp);
                            thumbGr.DrawImage(thumbBmp, 0, 0);

                            MemoryStream thumbMs = new MemoryStream();
                            thumbBmp.Save(thumbMs, System.Drawing.Imaging.ImageFormat.Jpeg);
                            long thumbmsLen = thumbMs.Length;
                            byte[] thumbData = new byte[thumbmsLen];
                            thumbData = thumbMs.ToArray();

                            System.IO.FileStream tStream = new System.IO.FileStream(Server.MapPath("SavedImages") + @"\thmb_" + Path.GetFileNameWithoutExtension(FileUpload1.PostedFile.FileName) + Path.GetExtension(FileUpload1.PostedFile.FileName), System.IO.FileMode.Create);
                            tStream.Write(thumbData, 0, thumbData.Length);
                            tStream.Close();

                            thumbGr.Dispose();
                            thumbBmp.Dispose();
                            thumbMs.Dispose();
                        }
                    }
                }
            }
        }

        // Returns an object with all codec data
        protected ImageCodecInfo GetImgCodecInf(string mimeType)
        {
            ImageCodecInfo[] imgCodecInfo = ImageCodecInfo.GetImageEncoders();

            foreach (ImageCodecInfo infoItem in imgCodecInfo)
            {
                if (infoItem.MimeType.ToString().ToLower() == mimeType.ToLower())
                {
                    return infoItem;
                }
            }

            return null;
        }
    }
}

Finally, control is returned back to the button click event handler where the SqlDataSource insert parameters are updated and a record is inserted into the table.  Since all of the action occurs during a postback, the GridView is automatically updated with the new record.

Conclusion

We now have uploaded a JPG image file, resized it smaller with the aspect ratio intact and saved it to disk with a corresponding thumbnail image.  Could this have been done differently? More efficiently?  Absolutely.  This is only one of dozens different techniques possible. I have successfully used this technique on several web projects.  Feel free to leave your comments and suggestions below.

History

Initial version, October 2014

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here