在asp.net mvc 3中使用上次修改的标头和OutputCacheAttribute进行客户端缓存
已编辑在asp.net mvc 3中使用上次修改的标头和OutputCacheAttribute进行客户端缓存,asp.net,asp.net-mvc,asp.net-mvc-3,caching,browser-cache,Asp.net,Asp.net Mvc,Asp.net Mvc 3,Caching,Browser Cache,已编辑 [HttpGet] [OutputCache( Duration = 7200, VaryByParam = "productId;lastUpdated", Location = OutputCacheLocation.Client)] public ActionResult GetImage(string productId, string lastUpdated) { var dir = Server.MapP
[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");
}
我想在客户机上缓存图像,并且知道在MVC3中有不同的方法来实现这一点:(如果我错了,请纠正我)
1) 您可以使用OutputCacheAttribute
,它在Expires
http头的帮助下工作。但它将返回304 Not Modified
,除非时间到期(即使图像已更改)
2) 为了避免显示过时的图像,可以使用Last Modified
http头(带有OutputCacheAttribute
)。在这种情况下,浏览器将请求发送到服务器,如果修改后带有,因为http头。在服务器上验证对象是否仍然有效,如果是,则返回Last Modified
http头(浏览器从本地缓存获取图像);如果修改了对象,则返回该对象时将显示200 OK
状态。
因此,浏览器每次从自己的缓存中获取图像之前都需要向服务器发送请求。-
3) 还有另一种方法(在我的例子中,我被告知正确的方法,因为图像将很少更改…无论如何,我需要完全实现这一点):将修改的日期添加到图像url,并将缓存设置为永久(1年或更长)。如果图像已更改,则应发送带有新版本的新url
代码如下:
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");
}
}
如果我使用上面的代码,浏览器似乎不会向服务器发送请求,而只是从缓存中获取图像,除非持续时间没有结束。(当我更改图像时,浏览器不会显示新版本)
问题:
<img src="@Url.Action("GetImage", "Home", new { productId = "test-product-100",
lastUpdated =Model.LastUpdated })" />
1)如何实现第三种方法,以便浏览器将从客户端缓存中获取图像(并且不会在每次需要图像时向服务器发送响应),除非图像被修改?
编辑:实际代码将不胜感激
2)在上述代码中,第一个图像请求的时间被写入最后一次修改(不知道为什么)。如何将文件的修改日期写入上次修改?
编辑:这个问题与第二种方法有关。另外,如果我只在客户端缓存并使用Last Modified
实现,则只有在按下F5
时,我才会获得304 Not Modified
状态。如果我重新输入相同的url,我将得到200 OK
。如果我在客户端缓存而不使用上次修改的。这是怎么解释的呢?如果我理解正确,您所追求的是无限缓存,并且依赖于通过更改资源的实际url使缓存无效
在这种情况下,我相信实际的实现要简单得多,并且不需要手动处理头部
最终,关键是能够通过URL加载图像,例如:
http://someDomain/product/image/1?v={something}
“1”是productId,并指定某种版本标识符(“v”)
关键是构建该url,其中v的值取决于图像的最后修改(您可能应该将其与图像或产品一起存储)。您可能可以散列修改日期,并使用它
如果下次构建url时,上次修改的日期相同,则将获得相同的哈希值,因此呈现与以前相同的url,浏览器将从缓存加载该url,而无需向服务器请求任何内容
一旦图像更新,并且修改日期更改,您的代码将生成不同的url,这将迫使浏览器再次请求
然后,您只需将OutputCache
属性应用于配置为在客户端缓存的操作(无需指定VaryByParam
),就可以进行设置。您可以使用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重新加载映像,而不是使用缓存的值
有关更多详细信息,请参阅。您可以研究使用ETags(),这是我阅读您的问题时想到的第一件事
您还可以在此处查看:最初使用的答案,但由于某些原因,当修改映像时,它不会在客户端中更新,而是显示映像的缓存版本
因此,解决方案是使用版本控制,这是您的第三个选项,只需从产品图像数据库中添加LastUpdated datetime字段即可
行动方法
[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 })" />
这个想法来自《邮报》
回答很晚,但希望能帮助一些人。我将图像存储在文件流中,所以我想我不能用图像存储修改后的日期,我如何获得它?我使用了File.GetLastWriteTime,但它不起作用:出于某种原因(即使我没有使用上次修改的筛选器实现),LastModified标头包含“上次访问”日期而不是“上次修改”日期。你是说在实际的文件系统上?如果你有足够的权限,GetLastWriteTime应该可以工作。如果调试代码,会看到GetLastWriteTime的无效值?另一个选项可能是存储图像上载日期,以及一些相关实体,例如产品。这也可以。是的,实际的文件流。在上面的代码中,我将此修改日期写入上次修改的标题中。但是每次我重新加载页面时,如果在请求中修改,那么我得到的页面是否等于上次响应中修改的页面?!这是什么原因呢?p、 我将为相关实体中的修改日期创建附加属性,但这个奇怪的L