2009-06-15 56 views
20

我使用IHttpHandler從數據庫提供圖像。相關的代碼是在這裏:來自HttpHandler的圖片不會在瀏覽器中緩存

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

的問題是,瀏覽器將不緩存的形象,大概是因爲我沒有指示在響應頭正確的事情。 HttpCachePolicy屬性的部分調用方法是我認爲會強制瀏覽器保持圖像,但它不。我認爲「正確」的事情是讓處理程序在沒有圖像的情況下返回304狀態碼,對吧?我如何實現使用IHttpHandler?

編輯:

每最好的答案,我得到這個代碼運行,它徹底解決了這個問題。是的,它需要一些重構,但它通常證明了我的所作所爲。相關部分:

if (!String.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); 
+0

它看起來像瀏覽器應緩存5分鐘。它是否緩存了這麼長時間?那是你的意圖嗎? – 2009-06-15 00:47:38

回答

23

AFAIK you負責發送304 Not Modified,這意味着我不知道.Net框架中的任何內容可以爲您發送「動態」圖像數據。你將需要做什麼(僞代碼):

  • 檢查請求中的If-Modified-Since標頭並解析出日期(如果存在)。
  • 將它與原始圖像(動態生成)圖像的最後修改日期進行比較。跟蹤這可能是解決這個問題最複雜的部分。在你目前的情況下,你正在重新創建每個請求的圖像;你不要想這樣做,除非你絕對必須。
  • 如果瀏覽器所擁有的文件的日期更新或等於您對映像的日期,請發送304未修改。
  • 否則,繼續使用當前的執行

一個簡單的方法來跟蹤你的最終最後修改時間是在文件系統緩存新生成的圖像,並保持在內存中的字典周圍的映射圖像ID到包含磁盤上的文件名和最後修改日期的結構。使用Response.WriteFile從磁盤發送數據。當然,每次你重啓你的工作進程時,字典都是空的,但你至少得到了一些緩存的好處,而不必在某處處理持久化緩存信息。

可以通過分離的「圖像一代」的關注和「通過HTTP發送圖片」到不同的類別支持這種做法。現在你在同一個地方做兩件完全不同的事情。

我知道這聽起來可能有點複雜,但它是值得的。我剛剛實施了這種方法,節省了處理時間和帶寬使用,令人難以置信。

0

你有什麼反應緩衝發生?如果是這樣,您可能需要在寫入輸出流之前設置標題。即嘗試將Response.OutputStream.Write()行向下移動到高速緩存設置線以下。

7

如果你有磁盤上,則可以使用此代碼的源文件:

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

此外,請確保您測試使用的是IIS,而不是從Visual Studio。 ASP。NET開發服務器(又名卡西尼)總是將Cache-Control設置爲私有。

參見:Caching Tutorial for Web Authors and Webmasters

6

這是它是如何在Roadkill's完成(一個.NET的wiki)文件處理程序:

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; 

托馬斯的有關IIS不提供狀態碼答案是關鍵,沒有它你每次只得到200回。

瀏覽器只會發送一個日期和時間,以表明它認爲該文件最後修改時間(根本沒有沒有標題),所以如果它不同,您只需返回一個200.您需要將文件的日期規範化爲刪除毫秒並確保它是UTC日期。

如果有一個有效的修改,我已經去了304s,但如果需要可以調整。