simple - ASP.NET MVC: hacer que las imágenes de caché del navegador de la acción
ejemplo de mvc asp net c# (5)
Tengo un método de acción que devuelve un archivo y solo tiene un argumento (una identificación).
p.ej
public ActionResult Icon(long id)
{
return File(Server.MapPath("~/Content/Images/image" + id + ".png"), "image/png");
}
Quiero que el navegador guarde automáticamente en caché esta imagen la primera vez que accedo a ella, así la próxima vez no tendrá que descargar todos los datos.
He intentado usar cosas como OutputCacheAttribute y establecer manualmente los encabezados en la respuesta. es decir:
[OutputCache(Duration = 360000)]
o
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetExpires(Cache.NoAbsoluteExpiration);
Pero la imagen todavía se carga cada vez que presiono F5 en el navegador (lo intento en Chrome e IE). (Sé que está cargado todo el tiempo porque si cambio la imagen también cambia en el navegador).
Veo que la respuesta HTTP tiene algunos encabezados que aparentemente deberían funcionar:
Cache-Control: public, max-age = 360000
Longitud del contenido: 39317
Tipo de contenido: image / png
Fecha: martes, 31 de enero de 2012 23:20:57 GMT
Vence: dom, 05 de febrero de 2012 03:20:56 GMT
Última modificación: mar, 31 ene 2012 23:20:56 GMT
Pero los encabezados de solicitud tienen esto:
Pragma: no-caché
¿Alguna idea de como hacerlo?
Muchas gracias
He usado la solución de @ user2273400, funciona, así que estoy publicando una solución completa.
Este es mi controlador con acción y método de ayuda temporal:
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);
}
}
}
Luego la clase de modelo:
public class FileModel
{
public byte[] File { get; set; }
public FileInfo FileInfo { get; set; }
public String ContentType { get; set; }
}
Y cómo lo estoy usando:
<img src="@Url.Action("Photo", "File", new { userId = 15 })" />
Lo primero que debe tener en cuenta es que cuando presiona F5 (actualizar) en Chrome, Safari o IE, las imágenes se volverán a solicitar, incluso si se almacenaron en la memoria caché del navegador.
Para decirle al navegador que no necesita descargar la imagen nuevamente, deberá devolver una respuesta 304 sin contenido, como se indica a continuación.
Response.StatusCode = 304;
Response.StatusDescription = "Not Modified";
Response.AddHeader("Content-Length", "0");
Sin embargo, querrá verificar el encabezado de la solicitud If-Modified-Since antes de devolver la respuesta 304. Por lo tanto, deberá verificar la fecha If-Modified-Since con la fecha modificada de su recurso de imagen (ya sea desde el archivo o almacenado en la base de datos, etc.). Si el archivo no ha cambiado, devuelva el 304; de lo contrario, devuelva con la imagen (recurso).
Estos son algunos buenos ejemplos de implementación de esta funcionalidad (estos son para un HttpHandler pero los mismos principios se pueden aplicar al método de acción MVC)
Prueba este código, funciona para mí
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);
Solo para informarle lo que he descubierto y me gustaría obtener una explicación o más información sobre cómo puedo saberlo.
Esto: Response.Cache.SetCacheability (HttpCacheability.Public); Response.Cache.SetExpires (Cache.NoAbsoluteExpiration);
de hecho, almacena en caché las imágenes .....
para probar esto ... prueba con la depuración ... pon el punto de interrupción en el controlador de imagen ... y verás que no se golpeará una vez ... incluso si F5 un par de veces ... pero qué es Es extraño para mí que todavía se devuelva una respuesta de 200.
Saque esas líneas y verá que comenzará a golpear nuevamente su punto de quiebre.
Mi pregunta: ¿Cuál es la forma correcta de decir que esta imagen está siendo servidor desde la memoria caché si aún recibe una respuesta de 200?
y esto estaba probando con IIS express 8. el IIS incorporado para Visual Studio no es GD ..
Editar : Leí mal la pregunta. Mi solución es para que el navegador no haga una nueva solicitud desde el servidor en la página abierta. Si el usuario presiona F5, el navegador solicitará los datos del servidor sin importar lo que haga con la información del caché. En ese caso, la solución es enviar un HTTP 304 como en la respuesta de @ brodie .
La solución más fácil que he encontrado para este problema fue usar OutputCacheAttribute.
Para su caso, debe usar más parámetros en el atributo 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");
}
El parámetro VaryByParam hace que el almacenamiento en caché se realice en función de la identificación. De lo contrario, se enviará la primera imagen para todas las imágenes. Debe cambiar el parámetro Duración de acuerdo con sus requisitos. El parámetro Ubicación hace que el almacenamiento en caché solo en el navegador. Puede establecer la propiedad de Ubicación en cualquiera de los siguientes valores: Any, Client, Downstream, Server, None, ServerAndClient. De forma predeterminada, la propiedad Ubicación tiene el valor Cualquiera.
Para información detallada, lea: