GetCacheDependency not called in custom VirtualPathProvider

491 views Asked by At

I've written a custom implementation of VirtualPathProvider which allows me to pull images from Azure Blob Storage. The provider does its thing but calls GetFile upon every request which isn't great for performance.

GetFile

public override VirtualFile GetFile(string virtualPath)
{
    string path = this.FormatVirtualPath(virtualPath);
    if (!path.StartsWith(this.pathPrefix, StringComparison.InvariantCultureIgnoreCase))
    {
        return base.GetFile(virtualPath);
    }

    string fileSystemPath = this.RemovePathPrefix(path);
    return new FileSystemVirtualFile(
                       virtualPath, 
                       () =>
                       this.fileSystem.Value.OpenFile(fileSystemPath));
}

Since I know the last modified date of the blob I thought it would be a good idea to add a bit of caching but I can't seem to get anything working.

Reading around it seems I need to override the methods GetCacheDependency and GetFileHash which I have done so as follows:

GetCacheDependency

public override CacheDependency GetCacheDependency(string virtualPath,
                                  IEnumerable virtualPathDependencies, 
                                  DateTime utcStart)
{
    string path = this.FormatVirtualPath(virtualPath);

    if (!path.StartsWith(this.pathPrefix, StringComparison.InvariantCultureIgnoreCase))
    {
        return base.GetCacheDependency(virtualPath, 
                                       virtualPathDependencies, 
                                       utcStart);
    }

    return new BlobCacheDependency(
                                 this.fileSystem
                                     .Value.GetLastModified(path)
                                     .DateTime.ToUniversalTime());
}

GetFileHash

public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
{
    string path = this.FormatVirtualPath(virtualPath);
    if (!path.StartsWith(this.pathPrefix, StringComparison.InvariantCultureIgnoreCase))
    {
        return base.GetFileHash(virtualPath, virtualPathDependencies);
    }

    byte[] bytes = Encoding.Unicode.GetBytes(virtualPath.ToCharArray());

    using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
    {
        byte[] hash = md5.ComputeHash(bytes);

        // Concatenate the hash bytes into one long String.
        return hash.Aggregate(
            new StringBuilder(32),
            (sb, b) => sb.Append(b.ToString("X2",
                                 CultureInfo.InvariantCulture)))
            .ToString().ToLowerInvariant();
    }
}

I also have a custom CacheDependency implementation.

public class BlobCacheDependency : CacheDependency
{
    public BlobCacheDependency(DateTime lastModifiedUtc)
    {
        this.SetUtcLastModified(lastModifiedUtc);
    }
}

Unfortunately neither the of those other methods ever get called when requesting the images. I'm not sure what is missing. Any ideas?

1

There are 1 answers

0
James South On BEST ANSWER

Ok so an answer to this is to set the response cache within the Open() method in my VirtualFile implementation. This allows the browser to cache the file.

It's a little hacky but works. In my code it looks like this now.

public override Stream Open()
{
    // Set the response headers here. It's a bit hacky.
    HttpCachePolicy cache = HttpContext.Current.Response.Cache;
    cache.SetCacheability(HttpCacheability.Public);
    cache.VaryByHeaders["Accept-Encoding"] = true;

    IFileSystem azureBlobFileSystem = FileSystemProviderManager.Current.GetUnderlyingFileSystemProvider("media");
    int maxDays = ((AzureBlobFileSystem)azureBlobFileSystem).FileSystem.MaxDays;

    cache.SetExpires(DateTime.Now.ToUniversalTime().AddDays(maxDays));
    cache.SetMaxAge(new TimeSpan(maxDays, 0, 0, 0));
    cache.SetRevalidation(HttpCacheRevalidation.AllCaches);

    return this.stream();
}

See

http://forums.asp.net/t/1745003.aspx?VirtualPathProvider+files+do+not+have+cache+control+headers