Mitch
Mitch

Reputation: 169

MVC3 Cache Images Stored in Database

I am displaying images on my website which I have stored in a database. From my Razor view I write a line such as this.

<img alt ="" src='@Url.Action("ShowImage",
            "Controller", new { imageID = Model.ImageID })'/>

The function that gets called looks like so.

    public ActionResult ShowImage(string imageID)
    {
        WebServiceClient client = new WebServiceClient();
        byte[] image = client.GetImage(Convert.ToInt64(imageID));
        if (image == null)
        {
            return new EmptyResult();
        }
        return File(image,"image/png");
    }

This works just fine, but my quesiton is if it is possible to cache these images somehow. I have seen ways to cache images using custom HttpHandler, but all those examples are if you have the actual file and not the binary data.

Upvotes: 1

Views: 938

Answers (2)

Paul
Paul

Reputation: 12440

Here is a cache rapper you could use:

public class CacheManager
{
    private static readonly string keyPrefix = typeof(CacheManager).FullName;
    private static readonly object syncLock = new object();

    private readonly Cache cache;

    public CacheManager(Cache cache)
    {
        this.cache = HttpRuntime.Cache;
    }

    public TValue Get<TValue>(string key)
    {
        return (TValue)HttpRuntime.Cache[MakeKey(key)];
    }

    public void Set<TValue>(string key, Func<TValue> value)
    {
        Set(key, null, null, null, value, null);
    }

    public void Set<TValue>(string key, DateTime timestamp, TValue value)
    {
        Set(key, timestamp, null, null, value, null);
    }

    public void Set<TValue>(string key, TimeSpan duration, TValue value)
    {
        Set(key, null, duration, null, value, null);
    }

    public void Set<TValue>(string key, TValue value, Action<bool> onRemoveCallback)
    {
        Set(key, null, null, null, value, onRemoveCallback);
    }

    public void Set<TValue>(string key, DateTime timestamp, TValue value, Action<bool> onRemoveCallback)
    {
        Set(key, timestamp, null, null, value, onRemoveCallback);
    }

    public void Set<TValue>(string key, TimeSpan duration, TValue value, Action<bool> onRemoveCallback)
    {
        Set(key, null, duration, null, value, onRemoveCallback);
    }

    public void Set<TValue>(string key, IEnumerable<string> fileDependencies, TValue value)
    {
        Set(key, null, null, fileDependencies, value, null);
    }

    public void Set<TValue>(string key, DateTime timestamp, IEnumerable<string> fileDependencies, TValue value)
    {
        Set(key, timestamp, null, fileDependencies, value, null);
    }

    public void Set<TValue>(string key, TimeSpan duration, IEnumerable<string> fileDependencies, TValue value)
    {
        Set(key, null, duration, fileDependencies, value, null);
    }

    public void Set<TValue>(string key, IEnumerable<string> fileDependencies, TValue value, Action<bool> onRemoveCallback)
    {
        Set(key, null, null, fileDependencies, value, onRemoveCallback);
    }

    public void Set<TValue>(string key, DateTime timestamp, IEnumerable<string> fileDependencies, TValue value, Action<bool> onRemoveCallback)
    {
        Set(key, timestamp, null, fileDependencies, value, onRemoveCallback);
    }

    public void Set<TValue>(string key, TimeSpan duration, IEnumerable<string> fileDependencies, TValue value, Action<bool> onRemoveCallback)
    {
        Set(key, null, duration, fileDependencies, value, onRemoveCallback);
    }

    public TValue GetOrCreate<TValue>(string key, Func<TValue> factory)
    {
        return GetOrCreate(key, null, null, null, factory, null);
    }

    public TValue GetOrCreate<TValue>(string key, DateTime timestamp, Func<TValue> factory)
    {
        return GetOrCreate(key, timestamp, null, null, factory, null);
    }

    public TValue GetOrCreate<TValue>(string key, TimeSpan duration, Func<TValue> factory)
    {
        return GetOrCreate(key, null, duration, null, factory, null);
    }

    public TValue GetOrCreate<TValue>(string key, Func<TValue> factory, Action<bool> onRemoveCallback)
    {
        return GetOrCreate(key, null, null, null, factory, onRemoveCallback);
    }

    public TValue GetOrCreate<TValue>(string key, DateTime timestamp, Func<TValue> factory, Action<bool> onRemoveCallback)
    {
        return GetOrCreate(key, timestamp, null, null, factory, onRemoveCallback);
    }

    public TValue GetOrCreate<TValue>(string key, TimeSpan duration, Func<TValue> factory, Action<bool> onRemoveCallback)
    {
        return GetOrCreate(key, null, duration, null, factory, onRemoveCallback);
    }

    public TValue GetOrCreate<TValue>(string key, IEnumerable<string> fileDependencies, Func<TValue> factory)
    {
        return GetOrCreate(key, null, null, fileDependencies, factory, null);
    }

    public TValue GetOrCreate<TValue>(string key, DateTime timestamp, IEnumerable<string> fileDependencies, Func<TValue> factory)
    {
        return GetOrCreate(key, timestamp, null, fileDependencies, factory, null);
    }

    public TValue GetOrCreate<TValue>(string key, TimeSpan duration, IEnumerable<string> fileDependencies, Func<TValue> factory)
    {
        return GetOrCreate(key, null, duration, fileDependencies, factory, null);
    }

    public TValue GetOrCreate<TValue>(string key, IEnumerable<string> fileDependencies, Func<TValue> factory, Action<bool> onRemoveCallback)
    {
        return GetOrCreate(key, null, null, fileDependencies, factory, onRemoveCallback);
    }

    public TValue GetOrCreate<TValue>(string key, DateTime timestamp, IEnumerable<string> fileDependencies, Func<TValue> factory, Action<bool> onRemoveCallback)
    {
        return GetOrCreate(key, timestamp, null, fileDependencies, factory, onRemoveCallback);
    }

    public TValue GetOrCreate<TValue>(string key, TimeSpan duration, IEnumerable<string> fileDependencies, Func<TValue> factory, Action<bool> onRemoveCallback)
    {
        return GetOrCreate(key, null, duration, fileDependencies, factory, onRemoveCallback);
    }

    public void Remove(string key)
    {
        HttpRuntime.Cache.Remove(MakeKey(key));
    }

    private static string MakeKey(string key)
    {
        Check.Argument.IsNotNullOrEmpty(key, "key");

        return keyPrefix + ":" + key;
    }

    private void Set<TValue>(string key, DateTime? timestamp, TimeSpan? duration, IEnumerable<string> fileDependencies, TValue value, Action<bool> onRemoveCallback)
    {
        string fullKey = MakeKey(key);

        lock (syncLock)
        {
            cache.Remove(fullKey);

            InsertInCache(fullKey, value, fileDependencies, timestamp, duration, onRemoveCallback);
        }
    }

    private TValue GetOrCreate<TValue>(string key, DateTime? timestamp, TimeSpan? duration, IEnumerable<string> fileDependencies, Func<TValue> factory, Action<bool> onRemoveCallback)
    {
        string fullKey = MakeKey(key);

        object value = cache.Get(fullKey);

        if (value == null)
        {
            lock (syncLock)
            {
                value = cache.Get(fullKey);

                if (value == null)
                {
                    value = factory();

                    if (value != null)
                    {
                        InsertInCache(fullKey, value, fileDependencies, timestamp, duration, onRemoveCallback);
                    }
                }
            }
        }

        return (TValue)value;
    }

    private void InsertInCache(string key, object value, IEnumerable<string> fileDependencies, DateTime? timestamp, TimeSpan? duration, Action<bool> onRemoveCallback)
    {
        Action<string, object, CacheItemRemovedReason> raiseOnRemoveCallback = (cacheKey, state, reason) => onRemoveCallback(reason == CacheItemRemovedReason.DependencyChanged);

        cache.Add(key, value, fileDependencies != null ? new CacheDependency(fileDependencies.ToArray()) : null, timestamp ?? Cache.NoAbsoluteExpiration, duration ?? Cache.NoSlidingExpiration, CacheItemPriority.Normal, onRemoveCallback != null ? new CacheItemRemovedCallback(raiseOnRemoveCallback) : null);
    }
}

You can now change your code like so:

public ActionResult ShowImage(string imageID)
    {
        CacheManager cacheManager = new CacheManager();
        byte[] image = cacheManager.GetOrCreate(
                    imageID, //Cache Key
                    DateTime.Now.AddMinutes(20), //Will be in Cache for 20 min
                    () => {
                    WebServiceClient client = new WebServiceClient();
                    return client.GetImage(Convert.ToInt64(imageID));
        }); //Delegate to call if the cache is empty

        if (image == null)
        {
            return new EmptyResult();
        }
        return File(image,"image/png");
    }

Upvotes: 2

Darin Dimitrov
Darin Dimitrov

Reputation: 1039498

You could try decorating your controller action with the [OutputCache] attribute:

[OutputCache(Duration = 3600, VaryByParam = "imageID")]
public ActionResult ShowImage(string imageID)
{
    WebServiceClient client = new WebServiceClient();
    byte[] image = client.GetImage(Convert.ToInt64(imageID));
    if (image == null)
    {
        return new EmptyResult();
    }
    return File(image,"image/png");
}

You might also decide where you want those images to be cached using the Location property. The default value is Any meaning that the output cache can be located on the browser client (where the request originated), on a proxy server (or any other server) participating in the request, or on the server where the request was processed.

Upvotes: 1

Related Questions