2009-06-15 7 views
20

Sto servendo un'immagine da un database utilizzando un IHttpHandler. Il codice di riferimento è qui:Immagine da HttpHandler non memorizzerà la cache nel browser

public void ProcessRequest(HttpContext context) 
{ 
    context.Response.ContentType = "image/jpeg"; 
    int imageID; 
    if (int.TryParse(context.Request.QueryString["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; 
} 

Il problema è che il browser non memorizza nella cache l'immagine, presumibilmente perché non sto indicando la cosa giusta nelle intestazioni di risposta. I metodi di chiamata delle parti sulla proprietà HttpCachePolicy sono ciò che pensavo avrebbe forzato il browser a mantenere l'immagine, ma non lo è. Penso che la cosa "giusta" sia che il gestore restituisca un codice di stato 304 senza un'immagine, giusto? Come ottengo quello usando IHttpHandler?

EDIT:

Per la migliore risposta, ho ottenuto questo codice in esecuzione e si risolve completamente il problema. Sì, ha bisogno di qualche refactoring, ma generalmente dimostra quello che cercavo. Le parti rilevanti:

+0

Sembra che il browser debba memorizzarlo nella cache per 5 minuti. Lo sta memorizzando nella cache anche per così tanto tempo? È questo che intendi? –

+0

Il browser non memorizza nella cache. Questo codice viene eseguito ogni volta che viene effettuata la richiesta. –

+0

Completamente non correlato, ma hai delle implicazioni sulle prestazioni quando usi questo approccio per servire le immagini? Soprattutto quando servi più immagini in una pagina web con IHttpHandler? – jishi

risposta

23

AFAIK, è l'invio di 304 Not Modified, ovvero non sono a conoscenza di alcunché nel framework .Net che lo fa per voi in questo caso d'uso in cui si inviano dati di immagine "dinamici". Cosa si dovrà fare (in pseudo codice):

  • Controllare l'intestazione If-Modified-Since nella richiesta e analizzare la data (se esistente).
  • Confrontalo con l'ultima data di modifica dell'immagine originale (generata dinamicamente). Tracciare questa è probabilmente la parte più complessa della soluzione a questo problema. Nella tua situazione attuale, stai ricreando l'immagine ad ogni richiesta; Si non fare voglio farlo a meno che non sia assolutamente necessario.
  • Se la data del file del browser è più recente o uguale a quella per l'immagine, inviare 304 Non modificato.
  • In caso contrario, continuare con la tua attuale implementazione

Un modo semplice per tenere traccia ultimi tempi modificati sul finale è quello di memorizzare nella cache nuove immagini generate sul file system e mantenere un dizionario in memoria intorno che associa il ID immagine a una struttura contenente il nome del file su disco e l'ultima data di modifica. Utilizzare Response.WriteFile per inviare i dati dal disco. Ovviamente, ogni volta che riavvii il tuo processo di lavoro, il dizionario sarà vuoto, ma otterrai almeno un certo vantaggio di memorizzazione nella cache senza dover gestire le informazioni di memorizzazione nella cache persistenti da qualche parte.

È possibile supportare questo approccio separando le preoccupazioni di "Generazione dell'immagine" e "Invio di immagini su HTTP" in diverse classi. In questo momento stai facendo due cose molto diverse nello stesso posto.

So che questo può sembrare un po 'complesso, ma ne vale la pena. Recentemente ho implementato questo approccio e il risparmio in termini di tempo di elaborazione e utilizzo della larghezza di banda è stato incredibile.

0

Si verifica un buffer di risposta? In tal caso, potresti voler impostare le intestazioni prima di scrivere nel flusso di output. Provate a spostare la linea Response.OutputStream.Write() in basso fino alle linee di impostazione della cache.

7

Se si dispone di file di origine su disco è possibile utilizzare questo codice:

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

Inoltre, assicurarsi che si prova utilizzando IIS, non da Visual Studio. ASP.NET Development Server (aka Cassini) imposta sempre Cache-Control su private.

Consulta anche: Caching Tutorial for Web Authors and Webmasters

6

Questo è come si fa a Roadkill's (un wiki NET) gestore di file:

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 risposta di Thomas su IIS non fornendo il codice di stato è la chiave, senza di essa hai solo 200 indietro ogni volta.

Il browser ti invierà semplicemente una data e un'ora per il momento in cui pensa che il file sia stato modificato l'ultima volta (nessuna intestazione), quindi se differisce devi solo restituire un 200. È necessario normalizzare la data del file su rimuovere i millisecondi e assicurarsi che sia una data UTC.

Sono andato per impostazione predefinita a 304 se è stata modificata una modifica, ma può essere modificata se necessario.

Problemi correlati