Image de HttpHandler ne cache pas dans le navigateur

Je sers une image d’une firebase database à l’aide d’un IHttpHandler. Le code correspondant est ici:

public void ProcessRequest(HttpContext context) { context.Response.ContentType = "image/jpeg"; int imageID; if (int.TryParse(context.Request.QuerySsortingng["id"], out imageID)) { var photo = new CoasterPhoto(imageID); if (photo.CoasterPhotoID == 0) context.Response.StatusCode = 404; else { byte[] imageData = GetImageData(photo); context.Response.OutputStream.Write(imageData, 0, imageData.Length); context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(5)); context.Response.Cache.SetLastModified(photo.SubmitDate); } } else context.Response.StatusCode = 404; } 

Le problème est que le navigateur ne mettra pas l’image en cache, probablement parce que je n’indique pas la bonne chose dans les en-têtes de réponse. La partie appelant des méthodes sur la propriété HttpCachePolicy est ce que je pensais obliger le navigateur à conserver l’image, mais ce n’est pas le cas. Je pense que la “bonne” chose est que le gestionnaire retourne un code de statut 304 sans image, non? Comment y parvenir en utilisant IHttpHandler?

MODIFIER:

Selon la meilleure réponse, ce code est en cours d’exécution et résout complètement le problème. Oui, il faut un peu de refactoring, mais cela montre généralement ce que je cherchais. Les parties pertinentes:

 if (!Ssortingng.IsNullOrEmpty(context.Request.Headers["If-Modified-Since"])) { CultureInfo provider = CultureInfo.InvariantCulture; var lastMod = DateTime.ParseExact(context.Request.Headers["If-Modified-Since"], "r", provider).ToLocalTime(); if (lastMod == photo.SubmitDate) { context.Response.StatusCode = 304; context.Response.StatusDescription = "Not Modified"; return; } } byte[] imageData = GetImageData(photo); context.Response.OutputStream.Write(imageData, 0, imageData.Length); context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.SetLastModified(photo.SubmitDate); 

Si je comprends bien, vous êtes responsable de l’envoi de 304 non modifiés, ce qui signifie que je ne suis au courant de rien dans le cadre .Net qui le fait pour vous dans le cas où vous envoyez des données d’image “dynamics”. Ce que vous devrez faire (en pseudo-code):

  • Recherchez l’en-tête If-Modified-Since dans la demande et parsingz la date (si elle existe).
  • Comparez-le à la date de dernière modification de votre image d’origine (générée dynamicment). Suivre cela est probablement la partie la plus complexe de la solution à ce problème. Dans votre situation actuelle, vous recréez l’image à chaque demande. vous ne voulez pas faire cela à moins d’y être absolument obligé.
  • Si la date du fichier du navigateur est plus récente ou égale à celle de l’image, envoyez un message 304 Non modifié.
  • Sinon, continuez avec votre implémentation actuelle

Un moyen simple de suivre les dernières heures modifiées de votre côté est de mettre en cache les images nouvellement générées sur le système de fichiers et de conserver un dictionnaire en mémoire autour qui mappe l’ID d’image sur une structure contenant le nom du fichier sur le disque et la date de dernière modification. Utilisez Response.WriteFile pour envoyer les données à partir du disque. Bien sûr, chaque fois que vous redémarrez votre processus de travail, le dictionnaire sera vide, mais vous obtiendrez au moins un avantage en termes de mise en cache sans avoir à gérer des informations de mise en cache persistantes quelque part.

Vous pouvez prendre en charge cette approche en séparant les problèmes de “génération d’images” et de “envoi d’images sur HTTP” dans différentes classes. En ce moment, vous faites deux choses très différentes au même endroit.

Je sais que cela peut sembler un peu complexe, mais ça vaut le coup. J’ai récemment mis en œuvre cette approche et les économies de temps de traitement et d’utilisation de la bande passante étaient incroyables.

Si vous avez un fichier source sur le disque, vous pouvez utiliser ce code:

 context.Response.AddFileDependency(pathImageSource); context.Response.Cache.SetETagFromFileDependencies(); context.Response.Cache.SetLastModifiedFromFileDependencies(); context.Response.Cache.SetCacheability(HttpCacheability.Public); 

Assurez-vous également que vous testez avec IIS et non depuis Visual Studio. ASP.NET Development Server (aka Cassini) définit toujours Cache-Control sur privé.

Voir aussi: Didacticiel de mise en cache pour les auteurs et les webmasters Web

Voici comment cela se fait dans le gestionnaire de fichiers de Roadkill (un wiki .NET):

 FileInfo info = new FileInfo(fullPath); TimeSpan expires = TimeSpan.FromDays(28); context.Response.Cache.SetLastModifiedFromFileDependencies(); context.Response.Cache.SetETagFromFileDependencies(); context.Response.Cache.SetCacheability(HttpCacheability.Public); int status = 200; if (context.Request.Headers["If-Modified-Since"] != null) { status = 304; DateTime modifiedSinceDate = DateTime.UtcNow; if (DateTime.TryParse(context.Request.Headers["If-Modified-Since"], out modifiedSinceDate)) { modifiedSinceDate = modifiedSinceDate.ToUniversalTime(); DateTime fileDate = info.LastWriteTimeUtc; DateTime lastWriteTime = new DateTime(fileDate.Year, fileDate.Month, fileDate.Day, fileDate.Hour, fileDate.Minute, fileDate.Second, 0, DateTimeKind.Utc); if (lastWriteTime != modifiedSinceDate) status = 200; } } context.Response.StatusCode = status; 

La réponse de Thomas sur le fait que IIS ne fournit pas le code d’état est la clé. Sans cela, vous ne récupérez que 200 unités à chaque fois.

Le navigateur vous enverra simplement une date et une heure pour indiquer quand il pense que le fichier a été modifié pour la dernière fois (pas d’en-tête du tout). Par conséquent, s’il diffère, vous retournez simplement 200. Vous devez normaliser la date de votre fichier pour supprimer les millisecondes et vous c’est une date UTC.

J’ai opté pour le paramètre par défaut à 304 s’il existe un fichier modifié depuis, mais cela peut être modifié si nécessaire.

Avez-vous une mémoire tampon de réponse en cours? Si c’est le cas, vous pouvez définir les en-têtes avant d’écrire dans le stream de sortie. En d’autres termes, essayez de déplacer la ligne Response.OutputStream.Write() vers le bas, sous les lignes du paramètre Cache.