1

Currently I am working on an .net MVC 5 project and I need to cache some data on hard drive. How can I cache data and pages on server's hard drive?

2 Answers2

2

You can implement custom OutputCacheProvider.

public class FileCacheProvider : OutputCacheProvider
{
    private string _cachePath;

    private string CachePath
    {
        get
        {
            if (!string.IsNullOrEmpty(_cachePath))
                return _cachePath;

            _cachePath = ConfigurationManager.AppSettings["OutputCachePath"];
            var context = HttpContext.Current;

            if (context != null)
            {
                _cachePath = context.Server.MapPath(_cachePath);
                if (!_cachePath.EndsWith("\\"))
                    _cachePath += "\\";
            }

            return _cachePath;
        }
    }

    public override object Add(string key, object entry, DateTime utcExpiry)
    {
        Debug.WriteLine("Cache.Add(" + key + ", " + entry + ", " + utcExpiry + ")");

        var path = GetPathFromKey(key);

        if (File.Exists(path))
            return entry;

        using (var file = File.OpenWrite(path))
        {
            var item = new CacheItem { Expires = utcExpiry, Item = entry };
            var formatter = new BinaryFormatter();
            formatter.Serialize(file, item);
        }

        return entry;
    }

    public override object Get(string key)
    {
        Debug.WriteLine("Cache.Get(" + key + ")");

        var path = GetPathFromKey(key);

        if (!File.Exists(path))
            return null;

        CacheItem item = null;

        using (var file = File.OpenRead(path))
        {
            var formatter = new BinaryFormatter();
            item = (CacheItem)formatter.Deserialize(file);
        }

        if (item == null || item.Expires <= DateTime.Now.ToUniversalTime())
        {
            Remove(key);
            return null;
        }

        return item.Item;
    }

    public override void Remove(string key)
    {
        Debug.WriteLine("Cache.Remove(" + key + ")");

        var path = GetPathFromKey(key);

        if (File.Exists(path))
            File.Delete(path);
    }

    public override void Set(string key, object entry, DateTime utcExpiry)
    {
        Debug.WriteLine("Cache.Set(" + key + ", " + entry + ", " + utcExpiry + ")");

        var item = new CacheItem { Expires = utcExpiry, Item = entry };
        var path = GetPathFromKey(key);

        using (var file = File.OpenWrite(path))
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(file, item);
        }
    }

    private string GetPathFromKey(string key)
    {
        return CachePath + MD5(key) + ".txt";
    }

    private string MD5(string s)
    {
        var provider = new MD5CryptoServiceProvider();
        var bytes = Encoding.UTF8.GetBytes(s);
        var builder = new StringBuilder();

        bytes = provider.ComputeHash(bytes);

        foreach (var b in bytes)
            builder.Append(b.ToString("x2").ToLower());

        return builder.ToString();
    }
}

and register your provider in web.config

<appSettings>
  <add key="OutputCachePath" value="~/Cache/" />
</appSettings>

<caching>
  <outputCache defaultProvider="FileCache">
    <providers>
      <add name="FileCache" type="MyCacheProvider.FileCacheProvider, MyCacheProvider"/>
    </providers>
  </outputCache>
</caching>
  • Thanks for the answer and your code is fine if the cache is located in a directory where the project is hosted. But what I need is to create the cache file in another folder outside the project directory (or may be in another disk drive). –  Jul 27 '16 at 03:19
  • You should change IIS settings. For IIS 7 running on Windows Server 2008 R2 ... In the IIS Manager, select the Application Pool under which your Web Site is running. Click "Advanced Settings". There will be an entry for Identity (it is under the Process Model section). Click it, provide credentials for your account that has permission to access the share. See following question for details - http://stackoverflow.com/questions/14611015/iis7-accessing-network-share – Alexander Anufriev Jul 27 '16 at 07:09
0

I did get Alexander's answer to work, but I'm going to add a few things here I learned on the way. It is using:

using System.Security.Cryptography;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;
using System.Web.Caching;

I needed to add this class, and it has to be serializable or it throws an error:

[Serializable]
public class CacheItem
{
    public DateTime Expires { get; set; }
    public object Item { get; set; }
}

I had to change the web.config entry to this, because the answer seems to be referencing a namespace that isn't in the code. Not exactly sure on that, and I couldn't find any documentation on this tag:

<outputCache defaultProvider="FileCache">
    <providers>
      <add name="FileCache" type="FileCacheProvider"/>
    </providers>
  </outputCache>

And this one might be kind of obvious, but when you start testing and using it, make sure that expiration time is in UTC time:

DateTime expireTime = DateTime.UtcNow.AddHours(1);

user1544428
  • 110
  • 1
  • 8