Я все ждал, ждал, ждал, когда же все-таки умные ребята, разрабатывающие System.Web.Optimization добавят поддержку автоматической загрузки содержимого бандла в контейнер Azure Storage. На днях вышла «1.0.0-beta2», но желаемого функционала так я и не нашел, а быстроты то хочется...
Как использовать?
<head>
@BlobStorageProvider.Render("~/mainlayout", BlobStorageProvider.ContentType.Style)
</head>
Как работает?
/// <summary>
/// Ссылка на кеш-хранилище Blob'а
/// </summary>
private const string CachedStorageLink = "http://cdn.m6lab.com/{0}/{1}";
В вышеприведенном коде указана ссылка на привязанный Caching к Blob'у. Скорость отклика у Blob'а оставляет желать лучшего, поэтому рекомендую использовать Caching для получения данных из Blob'а.
Сделать override на функцию, создающую Bundle не представляется возможным, поэтому пришлось использовать костыль:
// Данные файла на сервере
var content = new WebClient().DownloadString("{URL Вашего сайта}" + url);
Исходный код целиком:
public class BlobStorageProvider {
/// <summary>
/// Перечисление возможных типов загружаемого контента
/// </summary>
public enum ContentType {
Script,
Style
}
/// <summary>
/// Ссылка на кеш-хранилище Blob'а
/// </summary>
private const string CachedStorageLink = "http://cdn.m6lab.com/{0}/{1}";
/// <summary>
/// Название Blob-хранилища
/// </summary>
private const string ContainerName = "robofollower";
/// <summary>
/// Настройки подключения к Blob'у
/// </summary>
private static readonly CloudStorageAccount StorageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName={Storage Account};AccountKey={Primary Key}");
/// <summary>
/// Контейнер Blob'a
/// </summary>
private static readonly CloudBlobContainer Container = StorageAccount.CreateCloudBlobClient().GetContainerReference(ContainerName);
/// <summary>
/// Контейнер ссылок на CDN
/// </summary>
private static readonly Dictionary<string, string> CdnLinks = new Dictionary<string, string>();
/// <summary>
/// Загруза данных в Blob-контейнер
/// </summary>
/// <param name="virtualPath"></param>
/// <param name="contentType"></param>
/// <returns></returns>
private static void Upload(string virtualPath, ContentType contentType) {
// Ссылка на файл на сервере
var url = Scripts.Url(virtualPath);
// Данные файла на сервере
var content = new WebClient().DownloadString("{URL Вашего сайта}" + url);
// Удаляем лишние знаки
var formatted = url.ToString().Replace("/", "").Replace("?", "").Replace("v", "").Replace("=", "").Replace("-", "");
var blobContentType = string.Empty;
// Добавляем постфикс и настраиваем контейнер
switch (contentType) {
case ContentType.Script:
formatted += ".js";
blobContentType = "text/javascript";
break;
case ContentType.Style:
formatted += ".css";
blobContentType = "text/css";
break;
}
// Референс блоба и его настройки
var blob = Container.GetBlockBlobReference(formatted);
blob.Properties.CacheControl = "public, max-age=3600";
blob.Properties.ContentType = blobContentType;
// Загрузка в контейнер
blob.UploadFromStream(new MemoryStream(Encoding.ASCII.GetBytes(content)));
// Добавляем в локальный контейнер ссылку
CdnLinks.Add(virtualPath, string.Format(CachedStorageLink, ContainerName, formatted));
}
public static IHtmlString Render(string virtualPath, ContentType contentType) {
if (!CdnLinks.ContainsKey(virtualPath))
Upload(virtualPath, contentType);
switch (contentType) {
case ContentType.Style: {
var tag = new TagBuilder("link");
tag.MergeAttributes(new Dictionary<string, string> { { "href", CdnLinks[virtualPath] }, { "rel", "stylesheet" } });
return new HtmlString(tag.ToString(TagRenderMode.SelfClosing));
}
case ContentType.Script: {
var tag = new TagBuilder("script");
tag.MergeAttributes(new Dictionary<string, string> { { "src", CdnLinks[virtualPath] } });
return new HtmlString(tag.ToString(TagRenderMode.SelfClosing));
}
}
return null;
}
}
Автор: LuckyReveal