2012-01-31 19 views
25

Mam metodę action, która zwraca plik i ma tylko jeden argument (identyfikator).ASP.NET MVC: Utwórz obrazy pamięci podręcznej przeglądarki z działania

np.

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

chcę przeglądarka automatycznie Cache obraz pierwszy raz do niego dostęp, więc następnym razem nie trzeba pobierać wszystkie dane.

Próbowałem używać takich rzeczy jak OutputCacheAttribute i ręczne ustawianie nagłówków odpowiedzi. tj:

[OutputCache(Duration = 360000)] 

lub

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

ale obraz nadal jest ładowany za każdym razem uderzę F5 w przeglądarce (Próbuję go na Chrome i IE). (Wiem, że jest ładowany za każdym razem, ponieważ jeśli zmienię obraz, zmieni się również w przeglądarce).

widzę, że odpowiedź HTTP ma kilka nagłówków, które pozornie powinno działać:

Cache-Control: Miejski, max-age = 360000

Content-Length: 39317

Content Type: image/png

Data: Tue, 31 stycznia 2012 23:20:57 GMT

Wygasa: Sun, 05 lut 2012 03:20:56 GMT

Last-Modified: Tue, 31 stycznia 2012 23:20:56 GMT

Ale nagłówki żądania mieć to:

Pragma: no-cache

Każdy pomysł, jak to zrobić?

dziękuję

Odpowiedz

19

Pierwszą rzeczą, aby pamiętać, że kiedy trafisz F5 (Odśwież) w Chrome, Safari lub IE obrazy będą ponownie wniosek, nawet jeśli zostały one zapisywane w przeglądarce.

Aby poinformować przeglądarkę, że nie trzeba ponownie pobierać obrazu, należy zwrócić odpowiedź 304 bez zawartości, zgodnie z opisem poniżej.

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

Będziesz chciał sprawdzić nagłówek żądania If-Modified-Since przed zwróceniem odpowiedzi 304. Musisz więc sprawdzić datę If-Modified-Since w stosunku do daty modyfikacji zasobu obrazu (niezależnie od tego, czy pochodzi on z pliku, czy z bazy danych itp.). Jeśli plik się nie zmienił, zwróć 304, w przeciwnym razie zwróć z obrazem (zasobem).

Oto kilka dobrych przykładów realizacji tej funkcji (są na HttpHandler ale te same zasady mogą być stosowane metody działania MVC)

+2

dziękuję, właśnie to, czego szukasz. Wydaje się to jednak trochę skomplikowane, czy nie powinien istnieć "bardziej standardowy" sposób robienia tego przy użyciu ASP.NET MVC? Jak metoda, która otrzyma odpowiedź i najnowszą modyfikację i odpowiednio ją obsłuży? (Tak właśnie bym zrobił, ale nie sądzę, że jestem pierwszą osobą, która o tym myśli). – willvv

+1

Zawsze używałem własnego HttpHandler - więc nie trzeba używać MVC, ale nie oznacza to, że można to zrobić w ten sposób - po prostu nie wiesz, która metoda działa lepiej? Mogę polecić spojrzenie na http://imageresizing.net/, który jest świetnym, małym komponentem, który może obsłużyć wszystko dla ciebie i nie tylko! – brodie

+0

Problem polega na tym, że moje obrazy są przechowywane w bazie danych i mogą być modyfikowane zewnętrznie, dlatego obsługa HTTP nie wydaje się być opcją. – willvv

13

Try ten kod działa dla mnie

  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

Po prostu, aby poinformować, co odkryłem i chciałbym wyjaśnienie lub więcej informacji, jak mogę powiedzieć.

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

robi w rzeczywistości podręcznej obrazów .....

udowodnić to ... Test z debugowania ... umieścić punkt przerwania na kontrolerze obrazu ... i widać, że to nie zostanie trafiony raz ... nawet jeśli kilka razy F5 ... , ale dziwne jest dla mnie, że odpowiedź 200 wciąż jest zwracana.

Pozbądź się tych linii, a zobaczysz, że zaczniesz ponownie uderzać w punkt przerwania.

Moje pytanie: Jaki jest poprawny sposób poinformowania, że ​​ten obraz jest serwerem z pamięci podręcznej, jeśli wciąż otrzymujesz odpowiedź 200.

i to testował z IIS wyrazić 8. wbudowany w IIS dla Visual Studio nie jest Gd ..

1

Użyłem roztwór z @ user2273400, to działa, więc jestem delegowania kompletne rozwiązanie.

To jest mój kontroler z akcji i tymczasowej metody pomocnej:

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

    } 
} 

wówczas klasy Model:

public class FileModel 
{ 
    public byte[] File { get; set; } 
    public FileInfo FileInfo { get; set; } 
    public String ContentType { get; set; } 
} 

i jak używam go:

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

Edycja: Źle przeczytałem pytanie. Moje rozwiązanie polega na tym, że przeglądarka nie tworzy nowego żądania z serwera na stronie otwartej. Jeśli użytkownik naciśnie klawisz F5, przeglądarka zażąda danych z serwera bez względu na to, co zrobisz z informacjami o pamięci podręcznej. W takim przypadku rozwiązaniem jest send an HTTP 304 as in @brodie's answer.


Najprostszym rozwiązaniem, które znalazłem w tym problem był użycie OutputCacheAttribute.

Twoim przypadku musisz użyć więcej parametrów w atrybucie 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"); 
} 

parametr VaryByParam sprawia buforowanie wykonanej w oparciu o id.W przeciwnym razie pierwsze zdjęcie zostanie wysłane dla wszystkich zdjęć. Należy zmienić parametr Czas trwania zgodnie z wymaganiami. Parametr Location powoduje, że buforowanie odbywa się tylko w przeglądarce. Możesz ustawić właściwość Location na jedną z następujących wartości: Any, Client, Downstream, Server, None, ServerAndClient. Domyślnie właściwość Lokalizacja ma wartość Dowolny.

Szczegółowe informacje czytaj:

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

Powiązane problemy