ASP.NET MVC: сделать кеш браузера из действия - PullRequest
27 голосов
/ 01 февраля 2012

У меня есть метод действия, который возвращает файл и имеет только один аргумент (идентификатор).

например,

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 есть несколько заголовков, которые, очевидно, должны работать:* Контроль кэша: общедоступный, max-age = 360000

Длина содержимого: 39317

Тип содержимого: image / png

Дата: вт, 31 января 2012 г. 23: 20: 57 GMT

Срок действия истекает: Sun, 05 Feb 2012 03:20:56 GMT

Дата последнего изменения: вторник, 31 января 2012 23:20:56 GMT

Но заголовки запроса имеют следующее:

Pragma: no-cache

Есть идеи, как это сделать?

Большое спасибо

Ответы [ 5 ]

20 голосов
/ 01 февраля 2012

Первое, на что нужно обратить внимание, - это когда вы нажимаете F5 (обновить) в Chrome, Safari или IE, изображения будут запрашиваться снова, даже если они были кэшированы в браузере.

Чтобы сообщить браузеручто ему не нужно загружать изображение снова, вам нужно будет вернуть ответ 304 без содержимого, как показано ниже.

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

Вы захотите проверить запрос If-Modified-Sinceзаголовок перед возвратом ответа 304, хотя.Поэтому вам нужно сравнить дату If-Modified-Since с датой изменения вашего ресурса изображения (будь то из файла или из базы данных и т. Д.).Если файл не изменился, верните 304, в противном случае верните с изображением (ресурсом).

Вот несколько хороших примеров реализации этой функциональности (это для HttpHandler, но те же принципы могут быть применены кметод действия MVC)

13 голосов
/ 12 апреля 2013

Попробуйте этот код, он у меня работает

            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);
1 голос
/ 16 ноября 2014

Я использовал решение от @ 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 })" />
0 голосов
/ 09 января 2015

Редактировать : Я неправильно понял вопрос.Мое решение заключается в том, что браузер не делает новый запрос от сервера при открытой странице.Если пользователь нажимает клавишу F5, браузер будет запрашивать данные с сервера независимо от того, что вы делаете с информацией о кеше.В этом случае решение состоит в том, чтобы отправить HTTP 304, как в ответе @ brodie .


Самым простым решением, которое я нашел для этой проблемы, было использование 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 выполняет кэширование на основе идентификатора.В противном случае первое изображение будет отправлено для всех изображений.Вы должны изменить параметр Duration в соответствии с вашими требованиями.Параметр 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

0 голосов
/ 04 июля 2013

Просто, чтобы вы знали, что я обнаружил, и хотели бы получить объяснение или дополнительную информацию о том, как я могу сказать.

Это: Response.Cache.SetCacheability (HttpCacheability.Public); Response.Cache.SetExpires (Cache.NoAbsoluteExpiration);

на самом деле кэширует изображения .....

чтобы доказать это ... тест с отладкой ... установить точку останова на контроллере изображения ... и вы увидите, что он не будет поражен один раз ... даже если вы F5 пару раз ... но что странно для меня, так это то, что ответ 200 все еще возвращается.

Уберите эти строки, и вы увидите, что снова начнете достигать своей точки останова.

Мой вопрос: как правильно определить, что это изображение является сервером из кэша, если вы все еще получаете ответ 200.

и это было тестирование с IIS Express 8. встроенный IIS для Visual Studio не GD ..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...