I am trying to implement storage of Azure websites in an Azure CDN.
I copied the files to the CDN, preserving the folder structure, as it was in the original App_Themes folder.
I created VirtualPathProvider and the required Virtualdirectory and VirtualFile classes. The provider is registered in global.asax.
My problem is that the only file that appears to be a CDN is the skin file. All images, css, etc. Still referenced as if they were in the standard App_Themes structure. If I set a breakpoint in my code, then my OpenTheme Open method will be called only for the skin file.
Has anyone been able to implement such a solution?
Any ideas what I'm doing wrong?
[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)] [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.High)] public class RedirectAppThemes : VirtualPathProvider { private bool IsPathVirtual(string virtualPath) { String checkPath = VirtualPathUtility.ToAppRelative(virtualPath); return checkPath.StartsWith("~/App_Themes/", StringComparison.InvariantCultureIgnoreCase); } public override bool FileExists(string virtualPath) { if (IsPathVirtual(virtualPath)) { VirtualThemeFile file = new VirtualThemeFile(virtualPath); return file.Exists; } else { return Previous.FileExists(virtualPath); } } public override VirtualFile GetFile(string virtualPath) { if (IsPathVirtual(virtualPath)) { return new VirtualThemeFile(virtualPath); } else { return Previous.GetFile(virtualPath); } } public override bool DirectoryExists(string virtualDir) { if (IsPathVirtual(virtualDir)) { VirtualThemeDirectory dir = new VirtualThemeDirectory(virtualDir); return dir.Exists; } else { return Previous.DirectoryExists(virtualDir); } } public override VirtualDirectory GetDirectory(string virtualDir) { if (IsPathVirtual(virtualDir)) { return new VirtualThemeDirectory(virtualDir); } else { return Previous.GetDirectory(virtualDir); } } public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) { if (IsPathVirtual(virtualPath)) { return null; } else { return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); } } } [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] public class VirtualThemeDirectory : VirtualDirectory { private string cdnPath = "http://xxxxxxxxx.blob.core.windows.net/"; public VirtualThemeDirectory(string virtualPath) : base(virtualPath) { } public override IEnumerable Children { get { List<object> children = new List<object>(); string dir = this.VirtualPath.Replace("/App_Themes/", String.Empty); CloudBlobClient client = new CloudBlobClient(cdnPath); var blobs = client.ListBlobsWithPrefix(@"webinterfacethemes/" + dir ); foreach (CloudBlobDirectory directory in blobs.OfType<CloudBlobDirectory>()) { VirtualThemeDirectory vtd = new VirtualThemeDirectory(directory.Uri.AbsolutePath.Replace("/webinterfacethemes/", "/App_Themes/")); children.Add(vtd); } foreach (CloudBlob file in blobs.OfType<CloudBlob>()) { VirtualThemeFile vtf = new VirtualThemeFile(file.Uri.AbsolutePath.Replace("/webinterfacethemes/", "/App_Themes/")); children.Add(vtf); } return children; } } public override IEnumerable Directories { get { List<object> children = new List<object>(); string dir = this.VirtualPath.Replace("/App_Themes/", String.Empty); CloudBlobClient client = new CloudBlobClient(cdnPath); var blobs = client.ListBlobsWithPrefix(@"webinterfacethemes/" + dir); foreach (CloudBlobDirectory directory in blobs.OfType<CloudBlobDirectory>()) { VirtualThemeDirectory vtd = new VirtualThemeDirectory(directory.Uri.AbsolutePath.Replace("/webinterfacethemes/", "/App_Themes/")); children.Add(vtd); } return children; } } public override IEnumerable Files { get { List<object> children = new List<object>(); string dir = this.VirtualPath.Replace("/App_Themes/", String.Empty); CloudBlobClient client = new CloudBlobClient(cdnPath); var blobs = client.ListBlobsWithPrefix(@"webinterfacethemes/" + dir); foreach (CloudBlob file in blobs.OfType<CloudBlob>()) { VirtualThemeFile vtf = new VirtualThemeFile(file.Uri.AbsolutePath.Replace("/webinterfacethemes/", "/App_Themes/")); children.Add(vtf); } return children; } } public bool Exists { get { string dir = this.VirtualPath.Replace("/App_Themes/", String.Empty); CloudBlobClient client = new CloudBlobClient(cdnPath); if (client.ListBlobsWithPrefix("webinterfacethemes/" + dir).Count() > 0) return true; else return false; } } } [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] public class VirtualThemeFile : VirtualFile { private string cdnPath = "http://xxxxxxx.vo.msecnd.net/webinterfacethemes/"; public VirtualThemeFile(string VirtualPath) : base(VirtualPath) { } public override Stream Open() { string url = this.VirtualPath.Replace("/App_Themes/", cdnPath); HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(url); WebResponse myResp = myReq.GetResponse(); Stream stream = myResp.GetResponseStream(); return stream; } public bool Exists { get {
And in the Global.asax Application_start application:
RedirectAppThemes redirect = new RedirectAppThemes(); HostingEnvironment.RegisterVirtualPathProvider(redirect);