0

我正在尝试创建一个新的 MediaFileSystemProvider,它将媒体无缝存储在 Azure Blob 存储中。

我从 Umbraco v6.1 源代码中复制了 MediaFileSystem 类作为我的起点。

然后我编辑了 /config/FileSystemProviders.config 文件,插入了我的新类详细信息。

当我重新启动 Umbraco 时,会调用新类,但出现错误:

“找不到接受 0 个参数的 'mysite.core.umbracoExtensions.FileSystemProviders.AzureBlobStorageProvider, mysite.core' 类型的构造函数”

这是我的课:

...

[FileSystemProvider("media")]
public class AzureBlobStorageProvider : FileSystemWrapper
{
    private string rootUrl;
    CloudStorageAccount storageAccount;
    CloudBlobClient blobClient;
    CloudBlobContainer container;

    public AzureBlobStorageProvider(IFileSystem wrapped)
        : base(wrapped)
    {

        var constring = ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString;

        // Retrieve storage account from connection string.
        storageAccount = CloudStorageAccount.Parse(constring);

        // Create the blob client.
        blobClient = storageAccount.CreateCloudBlobClient();

        // Retrieve reference to a previously created container.
        container = blobClient.GetContainerReference("mymedia");

        //container.CreateIfNotExists();
        //container.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });

        rootUrl = "https://mysite.blob.core.windows.net/media";
    }

...

方法

知道我在这里做错了什么吗?

干杯

4

2 回答 2

4

考虑到我从这里的人们那里得到的所有帮助,我无法完成这一半 :)

我设置了一个新的 Umbraco v6.1.6 并确认 MediaService.Deleted 事件绝对不会被下面的代码触发/挂钩。我去看看如何提交错误...

对于其他有兴趣在 Azure 存储中存储 Umbraco 媒体项目的人,以下是我的做法。您可以使用“CDNEnabled”键打开/关闭 CDN 以显示图像内容,并使用“AzureCDNUploadEnabled”键打开/关闭上传,而无需每次都触摸您的视图。

仅供参考,Azure Blob 存储的实际 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 -->

这是事件处理程序:

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");
        }

    }
}

这是渲染助手:

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())" />

欢迎提出改进建议。

啊啊,回馈的感觉真好:)

于 2014-01-18T08:15:31.350 回答
1

为什么我花了几个小时试图找到答案,然后在我发布后很快就找到了?

问题有两个方面:

1)我应该一直在实施 IFileSystem(从 AmazonS3Provider 源中获得灵感)

2) 从 FileSystemProviders.config 文件传递​​的参数名称未包含在构造函数中

于 2013-07-02T03:25:14.257 回答