ASP.NET中的图像CDN

时间:2010-03-27 19:49:55

标签: c# asp.net httphandler cdn static-content

我正在将我的网络应用程序中的所有图像移动到CDN,但我希望能够轻松地打开或关闭CDN,而无需硬编码图像的路径。

我的第一个想法是为图像扩展添加一个HttpHandler,这取决于web.config中的变量(类似的东西)是从服务器还是从CDN提供图像。但是稍微给出了这个之后我认为我基本上已经排除了这一点,因为它会导致ASP.NET处理每个图像的请求,从而增加了开销,它实际上可以完全减轻使用CDN的好处。 / p>

另一种方法是,由于我的所有页面都是从基页类继承的,因此我可以在基类中创建一个函数,该函数根据web.config变量确定从哪个文件路径提供服务。然后我会在标记中做这样的事情:

<img src='<%= GetImagePath()/image.png' />

我认为这可能是我最终要做的事情,但对我来说似乎有点笨拙。我还设想旧的.NET错误导致无法修改控件集合的问题,因为“&lt;%=”虽然“&lt;%#”解决方案可能会有效。

关于如何实现这一点的任何想法或想法?

9 个答案:

答案 0 :(得分:2)

基于预优化假设,您已经驳回了编写HttpHandler的问题。我会重新审视这个并且肯定写一个简单的HttpHandler并测试它。您可能会发现您的Page方法解决方案可能会更慢,尤其是如果您涉及ASP预处理器。

HttpHandlers非常接近金属 - 这是IIS将请求交给ASP.Net的微不足道的开销。这将是一个比你提出的更优雅的解决方案,并且可能更具可扩展性,我愿意打赌 - 更快。

答案 1 :(得分:2)

您是否考虑过稍微简单的方法?

如果您的页面都是从基类继承的,那么您可以在包含CDN前置URL的属性上公开属性(或者,如果要关闭CDN,则向本地服务器公开)。然后,将前置URL存储在web.config中是一件小事:

public string PrependURLPath() {
 get { return ConfigurationManager.AppSettings["ImagePrependURL"].ToString(); }
}

<appSettings/>元素中,您只需选择前置网址即可,例如:

http://my.cdn.com/user/

或:

http://my.own.server.com/images/

非常简单!

然后,您可以根据示例对图像引用进行编码,但调用基页属性以显示所需的路径:

<img src='<%= this.BasePage.PrependURLPath() + [YourImagePath.png] %>'/>

我同意通过内联调用设置图像源很麻烦,但您可能会按照其他人的建议进行操作,然后遍历页面上的图像控件,随时更改前置URL。

即使你的页面目前只从System.Web.UI.Page继承,创建你自己的继承System.Web.Page的基类也很简单,然后在你的解决方案中对所有剩余的页面进行查找/替换

希望这有帮助。

答案 2 :(得分:2)

在这里很晚才称重,但我一直在寻找类似的解决方案。搜索谷歌健全检查我做了什么。不考虑HttpHandler方法,我所做的只是扩展ASP.net Image控件:

public class Img : Image
{
    public Img()
    {
        RelativePath = false;
    }

    public bool RelativePath { get; set; }

    public override string ImageUrl
    {
        get
        {
            if (RelativePath)
                return base.ImageUrl;

            return "http://some.configurable-value.com" + base.ImageUrl;
        } 
        set { base.ImageUrl = value; }
    }
}

它很粗糙,准备就绪,但它确实有效:)显然它应该依赖于一些可配置的值而不是字符串文字,但这不是一个很大的改变

答案 3 :(得分:1)

如果你使用标签显示你的图像你可以创建一个控制适配器,这些允许你改变.net控件渲染的方式或普遍改变他们这样的东西应该做的伎俩:

using System.Web.UI.WebControls.Adapters;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ExampleCode
{
    public class ImageAdapter : WebControlAdapter
    {
        private bool UseCdn
        {
            get { return true; } // Get value from config or anywhere else
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            Image image = (Image)Control;

            if (UseCdn)
            {
                // If using relative urls for images may need to handle ~
                image.ImageUrl = String.Format("{0}/{1}", "CDN URL", image.ImageUrl);
            }
         }
      }
 }

然后将浏览器文件添加到Web项目中的App_Browsers文件夹,如下所示:

<browsers>
    <browser refID="Default">
      <controlAdapters>
        <adapter
          controlType="System.Web.UI.WebControls.Image"
          adapterType="ExampleCode.ImageAdapter"
          />
      </controlAdapters>
    </browser>
</browsers>

答案 4 :(得分:0)

您可以循环所有控件并更改基类上的prerender事件中的图像URL ...

答案 5 :(得分:0)

HTTP Handler方法的好处在于它非常可重用和可配置:您可以根据位置识别要处理的img路径 - 假设它们所处的结构有助于此。

可能的缺点是图像文件扩展名(.jpg,.png等)不会自动传递到asp.net管道;您可以轻松配置IIS来执行此操作 - 但您需要在IIS上具有一定级别的contriol - 因此如果您在共享托管环境中,它可能不是一个选项。

答案 6 :(得分:0)

我将采用@Rhys方法进行图像控制。

大多数时候,我尝试使用背景图像css而不是使用图像控制。

之后,我将css和图像一起上传到云端,并在相对路径下正常工作。

答案 7 :(得分:0)

看起来还没有接受答案,所以这是我的建议。我有类似的问题处理透明地修改URL(到另一端,但我也想过将它用于CDN支持)。

这是一个旧的过滤器/模块,但它通过一些调整很好地满足了我的需求:http://www.paraesthesia.com/archive/2007/12/14/urlabsolutifiermodule---convert-urls-in-asp.net-output-to-absolute.aspx

你可以做的是做一个响应过滤器并用httpmodule挂钩(就像这个绝对模块那样)。如果您使用此模块+响应过滤器,您可以通过修改它的源来替换主机名/前缀所有URL以使用CDN,从而实现所需。

答案 8 :(得分:0)

我必须解决您的问题和另一个问题,即我不想在开发期间从CDN获取资源,但仅限于在生产服务器上部署网站时。 为了解决这个问题,我开发了一个ExpressionBuilder,它只在生产中预先设置了CDN URL。

<asp:Image ImageUrl="<%$ CdnUrl:/images/myimage.png %>" runat="server" />

在以前的代码中,CDN URL将仅在生产中添加。

namespace IdeaR.Web.Compilation
{
[ExpressionPrefix("CdnUrl")]
public class CdnUrlExpressionBuilder : ExpressionBuilder
{
    public static object GetCdnUrl(string expression, Type target, string entry)
    {
        var retvalue = expression;
        var productionUri = new Uri("http://www.myproductionurl.com",
            UriKind.Absolute);
        var currentUri = HttpContext.Current.Request.Url;
        var cdnUrl = "http://cdn.mycdn.com";

        // If this is a production website URL
        if (currentUri.Scheme == productionUri.Scheme &&
            currentUri.Host == productionUri.Host)
            retvalue = cdnUrl + expression;

        return retvalue;
    }

    public override CodeExpression GetCodeExpression(BoundPropertyEntry entry,
        object parsedData, ExpressionBuilderContext context)
    {
        var componentType = entry.DeclaringType;
        var expressionArray = new CodeExpression[3]
        {
            new CodePrimitiveExpression(entry.Expression.Trim()),
            new CodeTypeOfExpression(componentType),
            new CodePrimitiveExpression(entry.Name)
        };

        var descriptor = TypeDescriptor.GetProperties(componentType)
            [entry.PropertyInfo.Name];
        return new CodeCastExpression(descriptor.PropertyType,
            new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression(GetType()),
                "GetCdnUrl", expressionArray));
    }       
}
}

有关更多信息,我写了一篇关于此的文章 How to use a CDN in production but not during development