4

已编辑

我想在客户端缓存图像,并且知道在 mvc 3 中有不同的方法可以做到这一点:(如果我错了,请纠正我)

1)您可以在http 标头OutputCacheAttribute的帮助下使用 which 。Expires但它会返回304 Not Modified,除非时间到期(即使图像已更改)。

2) 为避免显示过时的图像,您可以使用Last-Modifiedhttp 标头(带OutputCacheAttribute)。在这种情况下,浏览器将请求发送到带有If-Modified-Sincehttp 标头的服务器。在服务器上,您验证对象是否仍然有效,如果是,您只需返回Last-Modifiedhttp 标头(浏览器从本地缓存中获取图像);如果对象已修改,您将其返回200 OK状态。
因此,浏览器每次从自己的缓存中获取图像之前都需要向服务器发送请求。这是一个例子-

3)还有另一种方法(在我的情况下,我被告知正确的方法,因为图像很少会改变......无论如何,我需要完全实现这一点):将修改日期添加到图像url并设置缓存Expires为永恒(1 年或更长时间)。如果图像已更改,您应该发送带有新版本的新网址。

这是代码:

public class LastModifiedCacheAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is FilePathResult)
        {
            var result = (FilePathResult)filterContext.Result;
            var lastModify = File.GetLastWriteTime(result.FileName);
            if (!HasModification(filterContext.RequestContext, lastModify))
                filterContext.Result = NotModified(filterContext.RequestContext, lastModify);
            SetLastModifiedDate(filterContext.RequestContext, lastModify);
        }
        base.OnActionExecuted(filterContext);
    }

    private static void SetLastModifiedDate(RequestContext requestContext, DateTime modificationDate)
    {
        requestContext.HttpContext.Response.Cache.SetLastModified(modificationDate);
    }

    private static bool HasModification(RequestContext context, DateTime modificationDate)
    {
        var headerValue = context.HttpContext.Request.Headers["If-Modified-Since"];
        if (headerValue == null)
            return true;
        var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
        return modifiedSince < modificationDate;
    }

    private static ActionResult NotModified(RequestContext response, DateTime lastModificationDate)
    {
        response.HttpContext.Response.Cache.SetLastModified(lastModificationDate);
        return new HttpStatusCodeResult(304, "Page has not been modified");
    }
}

LastModifiedCacheAttribute在 Global.asax 中注册了,并将以下 OutputCacheAttribute 应用于我的操作方法。

[HttpGet, OutputCache(Duration = 3600, Location = OutputCacheLocation.Client, VaryByParam = "productId")]
public FilePathResult GetImage(int productId)
{ // some code }

如果我使用上面的代码,浏览器似乎不会向服务器发送请求,而是只会从缓存中获取图像,除非持续时间没有结束。(当我更改图像时,浏览器不显示新版本)

问题:

1)如何实现第三种方法,以便浏览器从客户端缓存中获取图像(并且不会在每次想要图像时向服务器发送响应),除非图像被修改?
编辑:实际代码将不胜感激。

2)在上面的代码中,第一个图像请求的时间被写入Last-Modified(不知道为什么)。如何将文件的修改日期写入Last-Modified?
编辑:这个问题与第二种方法有关。此外,如果我只在客户端缓存并使用Last-Modified实现,我304 Not Modified只有在按下 时才会获得状态F5。如果我重新输入相同的网址,我会得到200 OK. 如果我在不使用的情况下缓存在客户端上,无论如何Last-Modified它都会返回。200 OK这怎么解释?

4

4 回答 4

1

您可以考虑使用 ETags ( http://en.wikipedia.org/wiki/HTTP_ETag ),这是我阅读您的问题时想到的第一件事。

你也可以看看这里:Set ETag for FileResult - MVC 3

于 2013-02-25T23:54:45.677 回答
0

您可以使用带有输出缓存的 VaryByCustom 选项来实现此目的,而无需使用自定义属性。像这样更改您的方法代码:

[HttpGet, OutputCache(Duration = 3600, 
    Location = OutputCacheLocation.Client, 
    VaryByCustom = "imagedate")]
public FilePathResult GetImage(int productId)
{ // some code }

然后将以下代码添加到您的 Global.asax:

    public override string GetVaryByCustomString(System.Web.HttpContext context, string custom)
    {
        if (custom.ToLower() == "imagedate")
        {
            return System.IO.File.GetLastWriteTime(Server.MapPath("~/Images/my-image.png")).ToString();
        }
        else
        {
            return base.GetVaryByCustomString(context, custom);
        }
    }

当图像文件的时间戳发生变化时,GetVaryByCustomString 方法的返回值将发生变化,这将导致 ASP.NET 重新加载图像而不是使用缓存的值。

有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/aa478965.aspx

于 2013-02-25T17:08:55.083 回答
0

如果我理解正确,那么您所追求的是无限缓存,并依赖于通过更改资源的实际 url 来使缓存无效。

在这种情况下,我相信实际的实现要简单得多,并且不需要手动处理标头。

最终,关键是能够通过 URL 加载图像,如下所示:

http://someDomain/product/image/1?v={something}

“1”是productId,并指定某种版本标识符(“v”)。

关键是构建该 url,其中v的值取决于图像的最后修改(您可能应该将其与图像或产品一起存储)。您可能可以散列修改日期,并使用它。

如果下次构建 url 时,上次修改的日期相同,您将获得相同的哈希值,因此呈现与以前相同的 url,并且浏览器将从缓存中加载它而不向服务器请求任何内容.

一旦图像更新并且修改日期发生变化,您的代码将生成一个不同的 url,这将迫使浏览器再次请求它。

然后你只需将OutputCache属性应用到动作,配置为在客户端缓存(无需指定VaryByParam),你应该设置。

于 2013-02-18T21:24:01.257 回答
0

最初使用这个答案,但由于某种原因,当图像被修改时,它不会在客户端更新,而是显示图像的缓存版本。

所以解决方案是使用版本控制,这是您的第三个选项,只需从您的产品图像数据库中添加 LastUpdated 日期时间字段

动作方法

    [HttpGet]
    [OutputCache(
    Duration = 7200,
    VaryByParam = "productId;lastUpdated",
    Location = OutputCacheLocation.Client)]
    public ActionResult GetImage(string productId, string lastUpdated)
    {
        var dir = Server.MapPath("~/productimages/");
        var path = Path.Combine(dir, productId + ".jpg");
        return base.File(path, "image/jpeg");
    }

在视图中

<img src="@Url.Action("GetImage", "Home", new { productId = "test-product-100", 
lastUpdated =Model.LastUpdated })" />

取自这篇文章的想法。

答案很晚,但希望可以帮助某人。

于 2016-03-26T16:00:10.993 回答