Click here to Skip to main content
15,512,053 members
Articles / Web Development / ASP.NET
Article
Posted 21 Oct 2014

Stats

27.3K views
751 downloads
34 bookmarked

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

Rate me:
Please Sign up or sign in to vote.
4.96/5 (14 votes)
21 Oct 2014CPOL4 min read
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.

C#
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:

C#
// 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:

C#
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:

C#
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:

C#
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:

C#
                            // 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.

Image 1

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) HP Enterprise Services
United States United States
A native Michigander, Steven is a Software Developer and Engineer with Hewlett-Packard Enterprise Services in Pontiac, Michigan and is currently assigned to Detroit Diesel Corporation, Detroit Michigan. He has been working in Information Technology in a variety of industries around the U.S. since 1995 after receiving a B.S. degree in Information Systems at Tarleton State University, Stephenville Texas, and an A.A.S. degree in Business Programming at Central Texas College, Killeen Texas. Prior to college, he served in the United States Army for over eight years overseas and at Fort Hood, Texas and participated in Operations Desert Shield and Desert Storm. He is the proud father of daughter Tina, a Special Education Teacher, and granddaughter Mia.

Comments and Discussions

 
SuggestionPretty straightforward article Pin
MadMyche14-Apr-17 2:16
professionalMadMyche14-Apr-17 2:16 
QuestionJust for my personal information ... Pin
RedDk3-Mar-15 9:24
RedDk3-Mar-15 9:24 
AnswerRe: Just for my personal information ... Pin
Steven T. Schowiak9-Apr-15 7:02
Steven T. Schowiak9-Apr-15 7:02 
GeneralRe: Just for my personal information ... Pin
RedDk9-Apr-15 7:19
RedDk9-Apr-15 7:19 
GeneralRe: Just for my personal information ... Pin
raddevus31-Jan-16 8:44
mvaraddevus31-Jan-16 8:44 
The URL is found in the .csproj file for your project.
It's auto-generated by Visual Studio to start IIS Express on for development.

You can also see the value by going to Project Properties in Visual Studio.
You can set the value on one of the project tabs.
GeneralRe: Just for my personal information ... Pin
RedDk31-Jan-16 9:11
RedDk31-Jan-16 9:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.