ここにいる人々から受けたすべての助けを考えると、この半分を終わらせることはできませんでした:)
新しい Umbraco v6.1.6 をセットアップし、以下のコードで MediaService.Deleted イベントが確実に発生/フックされないことを確認しました。バグを送信する方法を探しに行きます...
Umbraco メディア アイテムを Azure Storage に保存することに興味がある人のために、私が行った方法を以下に示します。「CDNEnabled」キーでイメージ コンテンツを表示するための CDN のオン/オフを切り替え、「AzureCDNUploadEnabled」キーでアップロードのオン/オフを切り替えることができます。毎回ビューに触れる必要はありません。
参考までに、Azure Blob Storage の実際の CDN 部分は現在利用できません。それは昔も今もそうではなく、いつかまたそうなるでしょう。
"AzureCDNCacheControlHeader" 値を設定して、アップロード時に Cache-Control ヘッダーを更新することで、データの使用を制限し、画像の配信を高速化できます。以下の値は、イメージが 30 日で期限切れになり、その後再検証されるように設定します。
これを web.config appsettings ノードに追加します。
<!-- cdn config -->
<!-- used for razor rendering -->
<add key="CDNPath" value="https://utest.blob.core.windows.net"/>
<add key="CDNEnabled" value="true"/>
<!-- used for media uploads -->
<add key="AzureCDNStorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName={yourAccount};AccountKey={yourKey}"/>
<add key="AzureCDNStorageAccountName" value="{yourStorageAccount}"/>
<add key="AzureCDNBlobContainerName" value="media"/>
<add key="AzureCDNRootUrl" value="https://{yourAccount}.blob.core.windows.net"/>
<add key="AzureCDNUploadEnabled" value="true"/>
<add key="AzureCDNCacheControlHeader" value="must-revalidate, public, max-age=604800"/> <!-- change to whatever suits you -->
<!-- end cdn -->
これは EventHandler です。
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Configuration;
using System.Web;
using System.Linq;
using System.IO;
using Umbraco.Core;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
namespace utest1.umbracoExtensions.events
{
public class SaveMediaToAzure : ApplicationEventHandler
{
/* either add your own logging class or remove this and all calls to 'log' */
private log4net.ILog log = log4net.LogManager.GetLogger(typeof(utest1.logging.PublicLogger));
CloudStorageAccount storageAccount;
private string blobContainerName;
CloudBlobClient blobClient;
CloudBlobContainer container;
string cacheControlHeader;
private bool uploadEnabled;
public SaveMediaToAzure()
{
try
{
storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["AzureCDNStorageConnectionString"]);
blobContainerName = ConfigurationManager.AppSettings["AzureCDNStorageAccountName"];
blobClient = storageAccount.CreateCloudBlobClient();
container = blobClient.GetContainerReference(ConfigurationManager.AppSettings["AzureCDNBlobContainerName"]);
uploadEnabled = Convert.ToBoolean(ConfigurationManager.AppSettings["AzureCDNUploadEnabled"]);
cacheControlHeader = ConfigurationManager.AppSettings["AzureCDNCacheControlHeader"];
MediaService.Saved += MediaServiceSaved;
MediaService.Trashed += MediaServiceTrashed;
MediaService.Deleted += MediaServiceDeleted; // not firing
}
catch (Exception x)
{
log.Error("SaveMediaToAzure Config Error", x);
}
}
void MediaServiceSaved(IMediaService sender, SaveEventArgs<IMedia> e)
{
if (uploadEnabled)
{
foreach (var fileItem in e.SavedEntities)
{
try
{
log.Info("Saving media to Azure:" + e.SavedEntities.First().Name);
var path = fileItem.GetValue("umbracoFile").ToString();
var filePath = HttpContext.Current.Server.MapPath(path);
UploadToAzure(filePath, path);
if (fileItem.GetType() == typeof(Umbraco.Core.Models.Media))
{
UploadThumbToAzure(filePath, path);
}
}
catch (Exception x)
{
log.Error("Error saving media to Azure: " + fileItem.Name, x);
}
}
}
}
/*
* Using this because MediaServiceDeleted event is not firing in v6.1.6
*
*/
void MediaServiceTrashed(IMediaService sender, MoveEventArgs<IMedia> e)
{
if (uploadEnabled)
{
try
{
log.Info("Deleting media from Azure:" + e.Entity.Name);
var path = e.Entity.GetValue("umbracoFile").ToString();
CloudBlockBlob imageBlob = container.GetBlockBlobReference(StripContainerNameFromPath(path));
imageBlob.Delete();
CloudBlockBlob thumbBlob = container.GetBlockBlobReference(StripContainerNameFromPath(GetThumbPath(path)));
thumbBlob.Delete();
}
catch (Exception x)
{
log.Error("Error deleting media from Azure: " + e.Entity.Name, x);
}
}
}
/*
* MediaServiceDeleted event not firing in v6.1.6
*
*/
void MediaServiceDeleted(IMediaService sender, DeleteEventArgs<IMedia> e)
{
//if (uploadEnabled)
//{
// try
// {
// log.Info("Deleting media from Azure:" + e.DeletedEntities.First().Name);
// var path = e.DeletedEntities.First().GetValue("umbracoFile").ToString();
// CloudBlockBlob imageBlob = container.GetBlockBlobReference(StripContainerNameFromPath(path));
// imageBlob.Delete();
// CloudBlockBlob thumbBlob = container.GetBlockBlobReference(StripContainerNameFromPath(GetThumbPath(path)));
// thumbBlob.Delete();
// }
// catch (Exception x)
// {
// log.Error("Error deleting media from Azure: " + e.DeletedEntities.First().Name, x);
// }
//}
Console.WriteLine(e.DeletedEntities.First().Name); // still not working
}
private string StripContainerNameFromPath(string path)
{
return path.Replace("/media/", String.Empty);
}
/*
*
*
*/
private void UploadToAzure(string filePath, string relativePath)
{
System.IO.MemoryStream data = new System.IO.MemoryStream();
System.IO.Stream str = System.IO.File.OpenRead(filePath);
str.CopyTo(data);
data.Seek(0, SeekOrigin.Begin);
byte[] buf = new byte[data.Length];
data.Read(buf, 0, buf.Length);
Stream stream = data as Stream;
if (stream.CanSeek)
{
stream.Position = 0;
CloudBlockBlob blob = container.GetBlockBlobReference(StripContainerNameFromPath(relativePath));
blob.UploadFromStream(stream);
SetCacheControl(blob);
}
else
{
log.Error("Could not read image for upload: " + relativePath);
}
}
private void SetCacheControl(CloudBlockBlob blob)
{
blob.Properties.CacheControl = cacheControlHeader;
blob.SetProperties();
}
private void UploadThumbToAzure(string filePath, string relativePath)
{
var thumbFilePath = GetThumbPath(filePath);
var thumbRelativePath = GetThumbPath(relativePath);
UploadToAzure(thumbFilePath, thumbRelativePath);
}
private string GetThumbPath(string path)
{
var parts = path.Split('.');
var filename = parts[parts.Length - 2];
return path.Replace(filename, filename + "_thumb");
}
}
}
これは RenderHelper です:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace utest1.umbracoExtensions.helpers
{
public class CDNImage
{
public static string ConvertUrlToCDN(string source)
{
if (String.IsNullOrEmpty(source))
{
return null;
}
var cdnUrl = System.Configuration.ConfigurationManager.AppSettings["CDNPath"];
var cdnOn = System.Configuration.ConfigurationManager.AppSettings["CDNEnabled"];
if (cdnOn == "true")
{
/*
* check if the url is absolute or not and whether it should be intercepted - eg. an external image url
* if it's absolute you'll need to strip out everything before /media...
*/
if (source.Contains(GetBaseUrl()))
{
source = StripBaseUrl(source);
}
}
return source;
}
private static string GetBaseUrl()
{
var url = System.Web.HttpContext.Current.Request.Url;
var baseUrl = url.Scheme + "//" + url.Host;
if (url.Port != 80 && url.Port != 443)
{
baseUrl += ":" + url.Port;
}
return baseUrl;
}
private static string StripBaseUrl(string path)
{
return path.Replace(GetBaseUrl(), String.Empty);
}
}
}
最後に RazorView に表示します。
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
Layout = "BasePage.cshtml";
}
@using utest1.umbracoExtensions.helpers
@{
var ms = ApplicationContext.Current.Services.MediaService;
var img = ms.GetById(int.Parse(CurrentPage.Image));
}
<h1>Umbraco on Azure is getting there!</h1>
<p>@img.Name</p>
<img alt="@img.Name" src="@CDNImage.ConvertUrlToCDN(img.GetValue("umbracoFile").ToString())" />
改善のための提案は大歓迎です。
ああ、お返しするのは気分がいいです:)