Подтвердить что ты не робот

ASP.NET MVC: Сделать изображения кеша браузера действиями

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

например.

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-ответ имеет некоторые заголовки, которые, по-видимому, должны работать:

Cache-Control: public, max-age = 360000

Content-Length: 39317

Content-Type: изображение /PNG

Дата: Вт, 31 Янв 2012 23:20:57 GMT

Истекает: Вс, 05 Фев 2012 03:20:56 GMT

Last-Modified: Вт, 31 Янв 2012 23:20:56 GMT

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

Pragma: нет кэша

Любая идея о том, как это сделать?

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

4b9b3361

Ответ 1

Прежде всего следует отметить, что когда вы нажимаете 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)

Ответ 2

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

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

Ответ 3

Я использовал решение из @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);
    }

    }
}

Затем класс Model:

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

Ответ 4

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

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

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

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

Возьмите эти строки, и вы увидите, что вы снова нажмете свою точку перерыва.

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

и это было тестирование с помощью IIS express 8. встроенный IIS для Visual Studio не является GD..

Ответ 5

Изменить. Я неправильно понял вопрос. Мое решение так, что браузер не делает новый запрос с сервера на странице открытым. Если пользователь нажимает 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