Asp.net mvc ASP.NET MVC:使浏览器缓存来自操作的图像

Asp.net mvc ASP.NET MVC:使浏览器缓存来自操作的图像,asp.net-mvc,http,caching,Asp.net Mvc,Http,Caching,我有一个actionmethod,它返回一个文件,并且只有一个参数(id) e、 g 我希望浏览器在我第一次访问该图像时自动缓存该图像,以便下次它不必下载所有数据 我尝试过使用OutputCacheAttribute和手动设置响应的标题。i、 e: [OutputCache(Duration = 360000)] 或 但每次我在浏览器上点击F5(我正在Chrome和IE上尝试)时,图像仍然会被加载。(我知道每次都会加载它,因为如果我更改图像,它也会在浏览器中更改) 我看到HTTP响应有一些显然

我有一个actionmethod,它返回一个文件,并且只有一个参数(id)

e、 g

我希望浏览器在我第一次访问该图像时自动缓存该图像,以便下次它不必下载所有数据

我尝试过使用OutputCacheAttribute和手动设置响应的标题。i、 e:

[OutputCache(Duration = 360000)]

但每次我在浏览器上点击F5(我正在Chrome和IE上尝试)时,图像仍然会被加载。(我知道每次都会加载它,因为如果我更改图像,它也会在浏览器中更改)

我看到HTTP响应有一些显然应该可以工作的头:

缓存控制:公共,最大年龄=360000

内容长度:39317

内容类型:图像/png

日期:2012年1月31日星期二23:20:57 GMT

过期时间:2012年2月5日星期日03:20:56 GMT

最后修改:2012年1月31日星期二23:20:56 GMT

但请求头具有以下特性:

Pragma:没有缓存

你知道怎么做吗


非常感谢

首先要注意的是,当您在Chrome、Safari或IE中点击F5(刷新)时,图像将再次被请求,即使它们已缓存在浏览器中

要告诉浏览器它不需要再次下载图像,您需要返回一个304响应,不包含任何内容,如下所示

Response.StatusCode = 304;
Response.StatusDescription = "Not Modified";
Response.AddHeader("Content-Length", "0");
在返回304响应之前,您需要检查If-Modified-Since请求头。因此,您需要根据图像资源的修改日期(无论是来自文件还是存储在数据库中,等等)检查是否修改了自日期。如果文件没有更改,则返回304,否则返回图像(资源)

下面是一些实现此功能的好例子(这些是针对HttpHandler的,但相同的原则可以应用于MVC操作方法)


试试这个代码,它对我有用

            HttpContext.Response.Cache.SetCacheability(HttpCacheability.Public);
            HttpContext.Response.Cache.SetMaxAge(new TimeSpan(1, 0, 0));

            Entities.Image objImage = // get your Image form your database

            string rawIfModifiedSince = HttpContext.Request.Headers.Get("If-Modified-Since");
            if (string.IsNullOrEmpty(rawIfModifiedSince))
            {
                // Set Last Modified time
                HttpContext.Response.Cache.SetLastModified(objImage.ModifiedDate);
            }
            else
            {
                DateTime ifModifiedSince = DateTime.Parse(rawIfModifiedSince);


                // HTTP does not provide milliseconds, so remove it from the comparison
                if (objImage.ModifiedDate.AddMilliseconds(
                            -objImage.ModifiedDate.Millisecond) == ifModifiedSince)
                {
                    // The requested file has not changed
                    HttpContext.Response.StatusCode = 304;
                    return Content(string.Empty);
                }
            }

            return File(objImage.File, objImage.ContentType);

只是想让你知道我发现了什么,并希望得到一个解释或更多的信息,我可以告诉你

这: Response.Cache.SetCacheability(HttpCacheability.Public); Response.Cache.SetExpires(Cache.NoAbsoluteExpiration)

实际上是否缓存图像

为了证明这一点。。。使用调试进行测试。。。在图像控制器上放置断点。。。 你会发现它不会被击中一次。。。即使你做了几次F5。。。 但令我感到奇怪的是,仍然返回200个响应

把这些线拿出来,你会发现你会再次开始达到你的断点

我的问题:如果您仍然收到200响应,那么如何正确地判断此映像正在从缓存中删除


这是用IIS express 8进行的测试。Visual studio的内置IIS不是GD。

我使用了@user2273400的解决方案,它可以工作,所以我发布了完整的解决方案

这是我的控制器,具有操作和临时帮助方法:

using System;
using System.Web;
using System.Web.Mvc;
using CIMETY_WelcomePage.Models;

namespace CIMETY_WelcomePage.Controllers
{
    public class FileController : Controller
    {

        public ActionResult Photo(int userId)
        {
            HttpContext.Response.Cache.SetCacheability(HttpCacheability.Public);
            HttpContext.Response.Cache.SetMaxAge(new TimeSpan(1, 0, 0));

            FileModel model = GetUserPhoto(userId);


            string rawIfModifiedSince = HttpContext.Request.Headers.Get("If-Modified-Since");
            if (string.IsNullOrEmpty(rawIfModifiedSince))
            {
                // Set Last Modified time
                HttpContext.Response.Cache.SetLastModified(model.FileInfo.LastWriteTime);
            }
            else
            {
                DateTime ifModifiedSince = DateTime.Parse(rawIfModifiedSince);


                // HTTP does not provide milliseconds, so remove it from the comparison
                if (TrimMilliseconds(model.FileInfo.LastWriteTime.AddMilliseconds) <= ifModifiedSince)
                {
                    // The requested file has not changed
                    HttpContext.Response.StatusCode = 304;
                    return Content(string.Empty);
                }
            }

            return File(model.File, model.ContentType);
        }

        public FileModel GetUserPhoto(int userId)
        {
            string filepath = HttpContext.Current.Server.MapPath("~/Photos/635509890038594486.jpg");
            //string filepath = frontAdapter.GetUserPhotoPath(userId);

            FileModel model = new FileModel();
            model.File = System.IO.File.ReadAllBytes(filepath);
            model.FileInfo = new System.IO.FileInfo(filepath);
            model.ContentType = MimeMapping.GetMimeMapping(filepath);

            return model;
        }

    private DateTime TrimMilliseconds(DateTime dt)
    {
        return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, 0);
    }

    }
}
我是如何使用它的:

<img src="@Url.Action("Photo", "File", new { userId = 15 })" />

编辑:我误读了这个问题。我的解决方案是,浏览器不会在打开页面时从服务器发出新请求。如果用户按F5键,浏览器将从服务器请求数据,无论您如何处理缓存信息。在这种情况下,解决办法是


我发现解决这个问题最简单的方法是使用OutputCacheAttribute

对于您的情况,必须在OutputCache属性中使用更多参数:

[OutputCache(Duration = int.MaxValue, VaryByParam = "id", Location=OutputCacheLocation.Client)]
public ActionResult Icon(long id)
{
    return File(Server.MapPath("~/Content/Images/image" + id + ".png"), "image/png");
}
VaryByParam参数根据id进行缓存。否则,将为所有图像发送第一个图像。 您应该根据需要更改持续时间参数。 Location参数仅在浏览器上进行缓存。您可以将Location属性设置为以下值之一:任意、客户端、下游、服务器、无、服务器和客户端。默认情况下,Location属性的值为Any

有关详细信息,请阅读:


非常感谢,这正是我想要的。这看起来确实有点复杂,难道不应该有一种“更标准”的方式来使用ASP.NETMVC吗?像一个接收响应和最新修改并进行相应处理的方法?(这是我会做的,但我不认为我是第一个想到这一点的人)。我一直使用我自己的HttpHandler-因此不需要使用MVC,但并不意味着它可以这样做-只是不确定哪种方式性能更好?我可以推荐看哪一个是一个伟大的小组件,可以处理一切为您和更多!问题是我的图像存储在数据库中,可以在外部修改,因此HTTP处理程序似乎不是一个选项。HttpHandler可以像任何其他可执行代码一样访问数据库或文件系统-只要权限正确,等等。我使用的HttpHandler确实:)因此,您的资源处理代码不需要是特定于MVC的-记住,MVC是在ASP.NET之上构建的。是的,我知道,但这意味着将特定于应用程序的代码放入我不喜欢的http处理程序中(我喜欢我的处理程序是通用的)。
public class FileModel
{
    public byte[] File { get; set; }
    public FileInfo FileInfo { get; set; }
    public String ContentType { get; set; }
}
<img src="@Url.Action("Photo", "File", new { userId = 15 })" />
[OutputCache(Duration = int.MaxValue, VaryByParam = "id", Location=OutputCacheLocation.Client)]
public ActionResult Icon(long id)
{
    return File(Server.MapPath("~/Content/Images/image" + id + ".png"), "image/png");
}