2012-01-31 77 views
25

我有一個actionmethod返回一個文件,只有一個參數(一個ID)。ASP.NET MVC:使動作的瀏覽器緩存圖像

例如

public ActionResult Icon(long id) 
{ 
    return File(Server.MapPath("~/Content/Images/image" + id + ".png"), "image/png"); 
} 

我希望瀏覽器在第一次訪問它時自動緩存該映像,以便下次不必下載所有數據。

我已經嘗試使用OutputCacheAttribute之類的東西並在響應中手動設置標題。即:

[OutputCache(Duration = 360000)] 

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

,但圖像仍然加載每次我打了F5在瀏覽器上(我想它在Chrome和IE)的時間。 (我知道它每次都被加載,因爲如果我更改圖像,它也會在瀏覽器中更改)。

我看到HTTP響應具有一定的頭,顯然應該工作:

緩存控制:公衆,最大年齡= 360000

的Content-Length:39317

內容 - 類型:image/PNG

日期:星期二,2012年1月31日23時二十分57秒GMT

過期:孫老師,2月5日2012 GMT 3時20分56秒

的Last-Modified:星期二,2012年1月31 23時20分56秒GMT

但請求頭有這樣的:

雜注:無緩存

有關如何做到這一點的任何想法?

非常感謝

回答

19

首先要注意的是,當你在Chrome,Safari或IE按F5(刷新)圖像將被再次請求,即使他們已經在瀏覽器中緩存。

要告訴瀏覽器它不需要再次下載圖像,您將需要返回一個沒有內容的304響應,如下所示。

Response.StatusCode = 304; 
Response.StatusDescription = "Not Modified"; 
Response.AddHeader("Content-Length", "0"); 

雖然在返回304響應之前您需要檢查If-Modified-Since請求標頭。因此,您需要根據圖像資源的修改日期(無論是來自文件還是存儲在數據庫中)檢查If-Modified-Since日期。如果文件沒有改變,則返回304,否則返回圖像(資源)。

下面是實現該功能(這些是用於HttpHandler的但相同的原理可以應用到MVC的操作方法)

+2

非常感謝,正是我一直在尋找。它似乎有點複雜,但不應該有一個「更標準」的方式來使用ASP.NET MVC?就像一個可以接收響應和最新修改並相應處理的方法一樣? (這就是我會做的,但我不認爲我是第一個想到這個問題的人)。 – willvv 2012-02-01 00:21:48

+1

我一直使用自己的HttpHandler - 所以不需要使用MVC,但並不意味着可以這樣做 - 只是不確定哪種方式更好?我可以推薦看一下http://imageresizing.net/,這是一個很棒的小組件,可以爲你處理所有事情和更多! – brodie 2012-02-01 00:37:04

+0

問題是我的圖像存儲在數據庫中,可以在外部進行修改,因此HTTP處理程序看起來不是一個選項。 – willvv 2012-02-01 02:41:00

13

嘗試的一些很好的例子這個代碼,它適用於我

  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); 
0

只是爲了讓你知道我發現了什麼,想要一個解釋或更多的信息,我可以告訴。

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

做其實緩存圖像.....

來證明這一點......測試與調試...把破發點的圖像打印控制器上...... ,你會看到它將不會被擊中一次...即使你F5幾次... 但我奇怪的是,200響應仍然返回。

取出這些線條,你會看到你會再次開始擊中你的折點。

我的問題:如果您仍然收到200響應,那麼告訴該映像正從緩存服務器的正確方法是什麼?

,這與IIS測試表達8.內置的IIS的Visual Studio是不GD ..

1

我已經使用來自@ 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); 
    } 

    } 
} 

那麼模型類:

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 })" /> 
-1

編輯:我誤解了這個問題。我的解決方案是,瀏覽器在打開頁面時不會從服務器發出新的請求。如果用戶按下F5鍵,則無論您如何處理緩存信息,瀏覽器都會向服務器請求數據。在這種情況下,解決方案是send an HTTP 304 as in @brodie's answer


我發現這個問題的最簡單的解決方案是使用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屬性設置爲以下任何一個值:Any,Client,Downstream,Server,None,ServerAndClient。默認情況下,Location屬性的值爲Any。

有關詳細信息,請閱讀:

http://www.asp.net/mvc/overview/older-versions-1/controllers-and-routing/improving-performance-with-output-caching-cs