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

On-demand loading of images from a database

0.00/5 (No votes)
16 May 2012 1  
Loading an image from a database isn't difficult, but it can waste time. If you are loading a large list of Images (or even thumbnails) from a DB but not using them immediately, then it can take some considerable time. Why not load them on demand instead?

Introduction

If you have a database which contains images - even if they are thumbnails - then loading them all on startup can take serious amount of time. For example, a recent application had a Picture class, with an Id, a Description, a thumbnail image, and a location for the full image all stored in a database. On startup, I needed to load the Pictures from the DB, so I could tie them together with the other classes. With around 1000 100x100 pixel jpg thumbnails in the DB, it was taking 13 to 15 seconds to load them all at startup - a silly amount of time.

So, instead, I changed it to on demand loading - which means that the time to load the image (from file or database) is deferred until the image itself is actually needed. Since this is generally as a result of user actions, we are then talking in terms of human reaction speeds, and 1000th of 15 seconds (plus the extra overhead of a DB connection setup) doesn't make any difference, people just don't notice the delay.

Using the code

It's pretty easy to do. Assuming your Picture class is written properly, you already have a Image property for the thumbnail:

public Image Thumbnail {get; private set;}

All you have to do is replace the auto-implemented backing field with a private version, and move the database access code into the property:

/// <summary>
/// Base of the Thumbnail property - this allows the full image to only be loaded on demand.
/// </summary>
private Image thumbnail = null;

/// <summary>
/// Thumbnail for the image
/// </summary>
public Image Thumbnail 
{
    get
    {
        if (thumbnail == null)
        {
            // Not loaded yet - load on demand
            using (SqlConnection con = new SqlConnection(GenericData.DBConnection))
            {
                con.Open();
                using (SqlCommand cmd = new SqlCommand("SELECT Thumbnail FROM Images WHERE Id=@ID", con))
                {
                    cmd.Parameters.AddWithValue("@ID", Id);
                    SqlDataReader r = cmd.ExecuteReader();
                    if (r.Read())
                    {
                        MemoryStream ms = new MemoryStream((byte[])r["Thumbnail"]);
                        thumbnail = GenericData.GetImage(ms);
                    }
                }
            }
        }
        return thumbnail;
    }
    private set
    {
        thumbnail = value;
    }
}

The GetImage method just creates an image from a Stream without requiring the stream to be open for the lifetime of the Image: 

/// <summary>
/// Get an Image without locking the file.
/// </summary>
/// <remarks>
/// Image.FromFile and Image.FromStream have problems in that the first
/// locks the file until the Image is Disposed, and the latter requires
/// the stream to be open and available until the Image is Disposed, which
/// effectively locks the file as well.
/// This doesn't, because it uses a copy of the image.
/// </remarks>
/// <param name="stream"></param>
/// <returns></returns>
public static Image GetImage(Stream stream)
{
    Image img;
    using (Image temp = Image.FromStream(stream))
    {
        img = new Bitmap(temp);
    }
    return img;
} 

Points of Interest

This is something I do quite often, I just wish I'd remember to do it in the first place each time...

History

Original version.

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