James South
James South

Reputation: 10645

Getting a bitmap from the cache

Whenever I try to get a bitmap I have cached from the context I am getting an argument exception. The bitmap is cast from the cached object but it's contents are corrupted.

The exception is thrown on the line

ImageFormat imageFormat = bitmap.RawFormat;

bitmap.RawFormat' threw an exception of type 'System.ArgumentException'

Which just gives me the message 'Parameter not valid.'

When I stick a breakpoint in the code look at the bitmap created from the cache all the properties report back the same exception.

Here's the process request from my handler....

    /// <summary>
    /// Processes the image request.
    /// </summary>
    /// <param name="context">The httpContext handling the request.</param>
    public void ProcessRequest(HttpContext context)
    {
        //write your handler implementation here.
        if (!string.IsNullOrEmpty(context.Request.QueryString["file"]))
        {
            string file = context.Request.QueryString["file"];
            bool thumbnail = Convert.ToBoolean(context.Request.QueryString["thumb"]);
            // Throw in some code to check width and height.
            int width = Convert.ToInt32(context.Request.QueryString["width"]);
            int height = Convert.ToInt32(context.Request.QueryString["height"]);

            // Store our context key.
            string key = file;

            // Strip away our cache data value.
            file = file.Substring(0, file.LastIndexOf("_", StringComparison.OrdinalIgnoreCase));

            OnServing(file);

            try
            {
                //Check the cache for a file.
                Bitmap bitmap = (Bitmap)context.Cache[key];
                if (bitmap != null)
                {
                    ImageFormat imageFormat = bitmap.RawFormat;
                    // We've found something so lets serve that.
                    using (MemoryStream ms = new MemoryStream())
                    {
                        bitmap.Save(ms, imageFormat);
                        context.Response.BinaryWrite(ms.ToArray());
                    }

                }
                else
                {
                    // Nothing found lets create a file.
                    // Lock the file to prevent access errors.
                    lock (this.syncRoot)
                    {
                        string path = HostingEnvironment.MapPath(String.Format("~/Images/{0}", file));
                        FileInfo fi = new FileInfo(path);
                        if (fi.Exists)
                        {
                            using (Bitmap img = (Bitmap)Bitmap.FromFile(path))
                            {
                                ImageFormat imageFormat = img.RawFormat;
                                if (thumbnail)
                                {
                                    ImageEditor imageEditor = new ImageEditor(img);
                                    Size size = new Size(width, height);
                                    imageEditor.Resize(size, true);
                                    imageEditor.FixGifColors();

                                    using (MemoryStream ms = new MemoryStream())
                                    {
                                        imageEditor.Image.Save(ms, imageFormat);

                                        // Add the file to the cache.
                                        context.Cache.Insert(key, img, new System.Web.Caching.CacheDependency(path));
                                        imageEditor.Dispose();
                                        context.Response.BinaryWrite(ms.ToArray());
                                    }
                                }
                                else
                                {
                                    using (MemoryStream ms = new MemoryStream())
                                    {
                                        img.Save(ms, imageFormat);

                                        // Add the file to the cache.
                                        context.Cache.Insert(key, img, new System.Web.Caching.CacheDependency(path));
                                        context.Response.BinaryWrite(ms.ToArray());
                                    }
                                }
                                OnServed(file);
                            }
                        }
                        else
                        {
                            OnBadRequest(file);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
                // OnBadRequest(ex.Message);
                // return a default empty file here.                    
            }

        }

    }

Any help would be greatly appreciated.

Upvotes: 2

Views: 3594

Answers (2)

Dave R.
Dave R.

Reputation: 7303

I can recommend not holding onto resources for longer than they're needed.

As for the code above, the issue is that you aren't creating a new bitmap when you get a saved image - you're retrieving an existing one from the cache, which has probably already been disposed of. Try using the following instead:

// When saving to the cache, use a MemoryStream to save off the bitmap as an array of bytes
using (MemoryStream ms = new MemoryStream()) {
    bitmap.Save(ms, bitmap.RawFormat);
    context.Cache.Insert(key, (byte[])ms.ToArray(), ...
    ...
}

...

// When retrieving, create a new Bitmap based on the bytes retrieved from the cached array
if (context.Cache[key] != null) {
    using (MemoryStream ms = new MemoryStream((byte[])context.Cache[key])) {
        using (Bitmap bmp = new Bitmap(ms)) {
            bmp.Save(context.Response.OutputStream ...
...

Upvotes: 3

Simon Steele
Simon Steele

Reputation: 11608

When you cache the image object you are doing so inside a using block:

using (Bitmap img = (Bitmap)Bitmap.FromFile(path))
{
    // ... lots of code

    // Add the file to the cache.
    context.Cache.Insert(key, img, new System.Web.Caching.CacheDependency(path));

    // ... other code
}

At the end of this using block, your bitmap will be disposed. Therefore you can't use it again. You need to stop disposing the bitmap if you want to use it again from the cache.

However, given that you just want to return the same image again if it's cached, it might be simpler and more efficient to just cache the MemoryStream - this doesn't suffer any invisible GDI+ ties and can be returned with almost no processing on a cache hit.

Upvotes: 2

Related Questions