Как загрузить вложенные и мини файлы в Windows Azure CDN
Я использую ASP.NET MVC 4, объединяющие и минимизирующие функции в пространстве имен Microsoft.AspNet.Web.Optimization
(например, @Styles.Render("~/content/static/css")
).
Я хотел бы использовать его в сочетании с CDN Windows Azure.
Я просмотрел пользовательский BundleTransform
, но контент пока еще не оптимизирован.
Я также изучал синтаксический анализ и загрузку оптимизированного потока во время выполнения, но для меня это похоже на хак, и мне это не очень нравится:
@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);
}
Как я могу автоматически загрузить связанные и минированные версии в свой CDN Windows Azure?
Ответы
Ответ 1
Таким образом, в настоящий момент нет отличного способа сделать это. Более продолжительный рабочий процесс, который мы рассматриваем, - это добавление поддержки связывания времени сборки. Затем вы должны запустить задачу сборки (или запустить exe, если хотите) для создания пакетов, а затем загрузить их в AzureCDN. Наконец, вы просто включаете UseCDN в BundleCollection, а помощники Script/Style просто переключатся на рендеринг ссылок на ваш AzureCDN с надлежащим резервным копированием в локальные пакеты.
В краткосрочной перспективе то, что я думаю, что вы пытаетесь сделать, это загрузить свой пакет в AzureCDN, когда пакет сначала сконструирован?
A BundleTransform - это один из способов сделать это, я думаю, это немного взломанный, но вы можете добавить BundleTransform в свой пакет. Начиная с последнего момента, BundleResponse.Content фактически является окончательным ответом на связку. В этот момент вы можете загрузить его на свой CDN. Это имеет смысл?
Ответ 2
Следуя рекомендациям Хао я Extended Bundle и IBundleTransform.
Добавление AzureScriptBundle или AzureStyleBundle к пакетам;
bundles.Add(new AzureScriptBundle("~/bundles/modernizr.js", "cdn").Include("~/Scripts/vendor/modernizr.custom.68789.js"));
Результаты в;
<script src="//127.0.0.1:10000/devstoreaccount1/cdn/modernizr.js?v=g-XPguHFgwIb6tGNcnvnI_VY_ljCYf2BDp_NS5X7sAo1"></script>
Если CdnHost не установлен, он будет использовать Uri blob вместо CDN.
Класс
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();
}
}
}
Ответ 3
Вы можете определить исходный домен как веб-сайт Azure (это, вероятно, было добавлено спустя много времени после исходного вопроса).
Как только у вас есть конечная точка CDN, вам нужно будет разрешить строку запроса для нее, а затем вы можете напрямую ссылаться на пакеты через CDN:
<link href="//az888888.vo.msecnd.net/Content/css-common?v=ioYVnAg-Q3qYl3Pmki-qdKwT20ESkdREhi4DsEehwCY1" rel="stylesheet"/>
Я также создал этот помощник, чтобы добавить имя хоста 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()));
}
Ответ 4
Для @manishKungwani, запрошенного в предыдущем комментарии. Просто установите UseCdn, а затем используйте
cdnHost перегрузка, чтобы собрать пучок. Я использовал это, чтобы разместить домен AWS CloudFront (xxx.cloudfront.net), но в ретроспективе его следовало назвать более общим для использования с любым другим поставщиком 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();
}
}