27 votes

Comment télécharger des fichiers groupés et minifiés sur Windows Azure CDN

Je suis à l'aide de la ASP.NET MVC 4 groupement et les minifying des fonctionnalités dans l' Microsoft.AspNet.Web.Optimization d'espace de noms (par exemple, @Styles.Render("~/content/static/css")).

J'aimerais l'utiliser en combinaison avec un Windows Azure CDN.

J'ai regardé dans la rédaction d'une coutume BundleTransform , mais le contenu n'est pas optimisé encore là.

J'ai aussi regardé dans l'analyse et le téléchargement de l'optimisation des flux de l'exécution, mais qui se sent comme un hack pour moi et je n'aime pas vraiment ça:

@StylesCdn.Render(Url.AbsoluteContent(
    Styles.Url("~/content/static/css").ToString()
    ));

public static IHtmlString Render(string absolutePath)
{
    // get the version hash
    string versionHash = HttpUtility.ParseQueryString(
        new Uri(absolutePath).Query
        ).Get("v");

    // only parse and upload to CDN if version hash is different
    if (versionHash != _versionHash)
    {
        _versionHash = versionHash;

        WebClient client = new WebClient();
        Stream stream = client.OpenRead(absolutePath);

        UploadStreamToAzureCdn(stream);
    }

    var styleSheetLink = String.Format(
        "<link href=\"{0}://{1}/{2}/{3}?v={4}\" rel=\"stylesheet\" type=\"text/css\" />",
        cdnEndpointProtocol, cdnEndpointUrl, cdnContainer, cdnCssFileName, versionHash
        );

    return new HtmlString(styleSheetLink);
}

Comment puis-je télécharger le logiciel et minimisé les versions automatiquement à mon Windows Azure CDN?

17voto

Daniel Franklin Points 125

Suivant les conseils de Hao, j'ai étendu Bundle et IBundleTransform.

Ajout d'AzureScriptBundle ou AzureStyleBundle aux bundles;

 bundles.Add(new AzureScriptBundle("~/bundles/modernizr.js", "cdn").Include("~/Scripts/vendor/modernizr.custom.68789.js"));
 

Résulte en;

 <script src="//127.0.0.1:10000/devstoreaccount1/cdn/modernizr.js?v=g-XPguHFgwIb6tGNcnvnI_VY_ljCYf2BDp_NS5X7sAo1"></script>
 

Si CdnHost n'est pas défini, il utilisera l'URI du blob au lieu du CDN.

Classe

 using System;
using System.Text;
using System.Web;
using System.Web.Optimization;
using System.Security.Cryptography;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;

namespace SiegeEngineWebRole.BundleExtentions
{
    public class AzureScriptBundle : Bundle
    {
        public AzureScriptBundle(string virtualPath, string containerName, string cdnHost = "")
            : base(virtualPath, null, new IBundleTransform[] { new JsMinify(), new AzureBlobUpload { ContainerName = containerName, CdnHost = cdnHost } })
        {
            ConcatenationToken = ";";
        }
    }

    public class AzureStyleBundle : Bundle
    {
        public AzureStyleBundle(string virtualPath, string containerName, string cdnHost = "")
            : base(virtualPath, null, new IBundleTransform[] { new CssMinify(), new AzureBlobUpload { ContainerName = containerName, CdnHost = cdnHost } })
        {
        }
    }

    public class AzureBlobUpload : IBundleTransform
    {
        public string ContainerName { get; set; }
        public string CdnHost { get; set; }

        static AzureBlobUpload()
        {
        }

        public virtual void Process(BundleContext context, BundleResponse response)
        {
            var file = VirtualPathUtility.GetFileName(context.BundleVirtualPath);

            if (!context.BundleCollection.UseCdn)
            {
                return;
            }
            if (string.IsNullOrWhiteSpace(ContainerName))
            {
                throw new Exception("ContainerName Not Set");
            }

            var conn = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("DataConnectionString"));
            var blob = conn.CreateCloudBlobClient()
                .GetContainerReference(ContainerName)
                .GetBlobReference(file);

            blob.Properties.ContentType = response.ContentType;
            blob.UploadText(response.Content);

            var uri = string.IsNullOrWhiteSpace(CdnHost) ? blob.Uri.AbsoluteUri.Replace("http:", "").Replace("https:", "") : string.Format("//{0}/{1}/{2}", CdnHost, ContainerName, file);

            using (var hashAlgorithm = CreateHashAlgorithm())
            {
                var hash = HttpServerUtility.UrlTokenEncode(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(response.Content)));
                context.BundleCollection.GetBundleFor(context.BundleVirtualPath).CdnPath = string.Format("{0}?v={1}", uri, hash);
            }
        }

        private static SHA256 CreateHashAlgorithm()
        {
            if (CryptoConfig.AllowOnlyFipsAlgorithms)
            {
                return new SHA256CryptoServiceProvider();
            }

            return new SHA256Managed();
        }
    }
}
 

14voto

Hao Kung Points 13035

Donc il n'y a pas une bonne façon de le faire actuellement. À plus long terme de flux de travail que nous envisageons est d'ajouter au moment de la construction groupement de soutien de. Ensuite, vous exécutez une tâche de génération (ou exécuter un fichier exe si vous préférez) pour générer les paquets et ensuite être en mesure de télécharger ces à la AzureCDN. Enfin, vous venez de tourner sur UseCDN sur le BundleCollection, et le Script/Style aides serait tout simplement de basculer automatiquement sur le rendu des liens menant à votre AzureCDN avec un bon secours pour votre local faisceaux.

Pour le court terme, je pense que vous essayez de faire est de télécharger votre application sur l'AzureCDN lorsque le module est construit en premier?

Un BundleTransform est une façon de le faire, je suppose, c'est un peu un hack, mais vous pouvez ajouter un BundleTransform dernière dans votre bundle. Depuis sa dernière, le BundleResponse.Le contenu est effectivement le dernier bundle réponse. À ce point dans le temps, vous pouvez le télécharger sur votre CDN. Cela fait-il sens?

2voto

Zack Z. Points 88

Pour @manishKungwani demandé dans le commentaire précédent. Définissez simplement UseCdn, puis utilisez la surcharge cdnHost pour créer le bundle. J'ai utilisé cela pour mettre dans un domaine AWS CloudFront (xxx.cloudfront.net) mais avec le recul, il aurait dû être nommé de manière plus générique pour être utilisé avec n'importe quel autre fournisseur CDN.

 public class CloudFrontScriptBundle : Bundle
{
    public CloudFrontScriptBundle(string virtualPath, string cdnHost = "")
        : base(virtualPath, null, new IBundleTransform[] { new JsMinify(), new CloudFrontBundleTransformer { CdnHost = cdnHost } })
    {
        ConcatenationToken = ";";
    }
}

public class CloudFrontStyleBundle : Bundle
{
    public CloudFrontStyleBundle(string virtualPath, string cdnHost = "")
        : base(virtualPath, null, new IBundleTransform[] { new CssMinify(), new CloudFrontBundleTransformer { CdnHost = cdnHost } })
    {
    }
}

public class CloudFrontBundleTransformer : IBundleTransform
{
    public string CdnHost { get; set; }

    static CloudFrontBundleTransformer()
    {
    }

    public virtual void Process(BundleContext context, BundleResponse response)
    {
        if (context.BundleCollection.UseCdn && !String.IsNullOrWhiteSpace(CdnHost))
        {
            var virtualFileName = VirtualPathUtility.GetFileName(context.BundleVirtualPath);
            var virtualDirectory = VirtualPathUtility.GetDirectory(context.BundleVirtualPath);

            if (!String.IsNullOrEmpty(virtualDirectory))
                virtualDirectory = virtualDirectory.Trim('~');

            var uri = string.Format("//{0}{1}{2}", CdnHost, virtualDirectory, virtualFileName);
            using (var hashAlgorithm = CreateHashAlgorithm())
            {
                var hash = HttpServerUtility.UrlTokenEncode(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(response.Content)));
                context.BundleCollection.GetBundleFor(context.BundleVirtualPath).CdnPath = string.Format("{0}?v={1}", uri, hash);
            }
        }
    }

    private static SHA256 CreateHashAlgorithm()
    {
        if (CryptoConfig.AllowOnlyFipsAlgorithms)
        {
            return new SHA256CryptoServiceProvider();
        }

        return new SHA256Managed();
    }
}
 

2voto

Jenya Y. Points 11

Vous pouvez définir le domaine d'origine comme le site Web d'Azure (cela a probablement été ajouté longtemps après la question d'origine).

Une fois que vous avez un point de terminaison CDN, vous devrez autoriser la chaîne de requête pour cela, puis vous pourrez faire référence directement aux bundles via CDN:

 <link href="http://stackoverflow.com//az888888.vo.msecnd.net/Content/css-common?v=ioYVnAg-Q3qYl3Pmki-qdKwT20ESkdREhi4DsEehwCY1" rel="stylesheet"/>
 

J'ai également créé cet assistant pour ajouter le nom d'hôte CDN:

 public static IHtmlString RenderScript(string virtualPath)
{
    if (HttpContext.Current.IsDebuggingEnabled)
        return Scripts.Render(virtualPath);
    else
        return new HtmlString(String.Format(
            CultureInfo.InvariantCulture, 
            Scripts.DefaultTagFormat, 
            "//CDN_HOST" + Scripts.Url(virtualPath).ToHtmlString()));
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X