Использование VirtualPathProvider для размещения тем в CDN Azure - PullRequest
4 голосов
/ 17 ноября 2011

Я пытаюсь реализовать хранение тем веб-сайта Azure в Azure CDN.

Я скопировал файлы в CDN, сохранив структуру папок, как это было в исходной папке App_Themes.

Я создал VirtualPathProvider и необходимые классы Virtualdirectory и VirtualFile. Провайдер зарегистрирован в global.asax.

Моя проблема в том, что единственный файл, который, кажется, приходит из CDN, это файл скина. Все изображения, CSS и т. Д. По-прежнему ссылаются так, как если бы они были в стандартной структуре App_Themes. Если я добавлю точку останова в свой код, тогда метод Open моей VirtualTheme всегда будет вызываться только для файла скина.

Кому-нибудь удалось реализовать подобное решение?

Есть идеи, что я делаю не так?

[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
        {
            //Check if the file exists
            //do this with a HEAD only request so we don't download the whole file
            string url = this.VirtualPath.Replace("/App_Themes/", cdnPath);
            HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(url);
            myReq.Method = "HEAD";
            HttpWebResponse myResp = (HttpWebResponse)myReq.GetResponse();
            if (myResp.StatusCode == HttpStatusCode.OK)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

А в Global.asax Application_start:

RedirectAppThemes redirect = new RedirectAppThemes();
        HostingEnvironment.RegisterVirtualPathProvider(redirect);

1 Ответ

3 голосов
/ 18 ноября 2011

Mucho Googling в итоге обнаружил эту похожую проблему

Я добавил следующее в свой web.config внутри тегов <system.webServer>, и теперь файлы изображений и css вызывают методы в моем коде.

<handlers>
  <add name="Images" path="*.png" verb="GET,HEAD,POST" type="System.Web.StaticFileHandler" modules="ManagedPipelineHandler" resourceType="Unspecified" />
  <add name="Stylesheets" path="*.css" verb="GET,HEAD,POST" type="System.Web.StaticFileHandler" modules="ManagedPipelineHandler" resourceType="Unspecified" />
</handlers>

Если кто-то ищет полное решение для размещения своих тем в CDN с использованием этого метода, я также изменил следующее из исходного кода, опубликованного выше, и теперь он работает:

класс VirtualFile выглядит следующим образом:

[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public class VirtualThemeFile : VirtualFile
{
    private string cdnPath = "https://xxxxxx.vo.msecnd.net/webinterfacethemes/";
    private string blobURL;

    public VirtualThemeFile(string VirtualPath) : base(VirtualPath)
    {
        blobURL = this.VirtualPath.Replace("/App_Themes/", cdnPath);
    }

    public override Stream Open()
    {
        CloudBlobClient client = new CloudBlobClient(cdnPath);
        CloudBlob blob = client.GetBlobReference(blobURL);
        MemoryStream stream = new MemoryStream();
        blob.DownloadToStream(stream);
        stream.Seek(0, SeekOrigin.Begin);
        return stream;
    }

    public bool Exists
    {
        get
        {
            CloudBlobClient client = new CloudBlobClient(cdnPath);
            CloudBlob blob = client.GetBlobReference(blobURL);
            try
            {
                blob.FetchAttributes();
                return true;
            }
            catch (StorageClientException e)
            {
                if (e.ErrorCode == StorageErrorCode.ResourceNotFound)
                {
                    return false;
                }
                else
                {
                    throw;
                }
            }
        }
    }
}

И я изменил свой код global.asax, чтобы VirtualPathProvider вызывался, если сайт предварительно скомпилирован:

HostingEnvironment hostingEnvironmentInstance = (HostingEnvironment)typeof(HostingEnvironment).InvokeMember("_theHostingEnvironment", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField, null, null, null);
        MethodInfo mi = typeof(HostingEnvironment).GetMethod("RegisterVirtualPathProviderInternal", BindingFlags.NonPublic | BindingFlags.Static);
        mi.Invoke(hostingEnvironmentInstance, new object[] { new RedirectAppThemes() });
...