2009-06-15 31 views
20

Tôi đang phục vụ một hình ảnh từ cơ sở dữ liệu bằng IHttpHandler. Mã có liên quan ở đây:Hình ảnh từ HttpHandler sẽ không lưu trong bộ nhớ cache

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

Vấn đề là trình duyệt sẽ không lưu hình ảnh, có lẽ vì tôi không cho biết điều đúng trong tiêu đề phản hồi. Các phương thức gọi một phần trên thuộc tính HttpCachePolicy là những gì tôi nghĩ sẽ buộc trình duyệt giữ lại hình ảnh, nhưng nó không. Tôi nghĩ rằng điều "đúng" là để người xử lý trả lại mã trạng thái 304 mà không có hình ảnh, phải không? Làm thế nào để đạt được điều đó bằng cách sử dụng IHttpHandler?

EDIT:

mỗi câu trả lời tốt nhất, tôi có mã này chạy và nó hoàn toàn giải quyết vấn đề. Có, nó cần một số tái cấu trúc, nhưng nó thường chứng minh những gì tôi đã sau. Các bộ phận liên quan:

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

Dường như trình duyệt sẽ lưu trong bộ nhớ cache trong 5 phút. Là nó bộ nhớ đệm nó cho dù đó dài? Đó là những gì bạn có ý định? –

+0

Trình duyệt không cache chút nào. Mã này thực hiện mỗi khi yêu cầu được thực hiện. –

+0

Hoàn toàn không liên quan, nhưng bạn có bất kỳ tác động hiệu quả nào khi sử dụng phương pháp này để phân phát hình ảnh không? Đặc biệt là khi bạn phục vụ một số hình ảnh trong một trang web với IHttpHandler của bạn? – jishi

Trả lời

23

AFAIK, bạn chịu trách nhiệm gửi 304 Không được sửa đổi, nghĩa là tôi không biết bất kỳ điều gì trong khung .Net thực hiện điều đó cho bạn trong trường hợp sử dụng này khi bạn gửi dữ liệu hình ảnh "động". Những gì bạn sẽ phải làm (trong mã giả):

  • Kiểm tra tiêu đề If-Modified-Since trong yêu cầu và phân tích ngày (nếu có).
  • So sánh nó với ngày sửa đổi cuối cùng của hình ảnh ban đầu (được tạo theo kiểu động) của bạn. Theo dõi điều này có lẽ là phần phức tạp nhất của giải pháp cho vấn đề này. Trong tình huống hiện tại của bạn, bạn đang tạo lại hình ảnh theo mọi yêu cầu; bạn không muốn làm điều đó trừ khi bạn hoàn toàn phải làm vậy.
  • Nếu ngày của tệp có trình duyệt mới hơn hoặc bằng với những gì bạn có cho hình ảnh, hãy gửi 304 Không được sửa đổi.
  • Nếu không, tiếp tục với thực hiện hiện tại của bạn

Một cách đơn giản để theo dõi lần sửa đổi lần cuối vào cuối cùng của bạn là để bộ nhớ cache hình ảnh mới được tạo ra trên hệ thống tập tin và giữ một từ điển trong bộ nhớ xung quanh mà các bản đồ ID hình ảnh vào một cấu trúc có chứa tên tệp trên đĩa và ngày sửa đổi cuối cùng. Sử dụng Response.WriteFile để gửi dữ liệu từ đĩa. Tất nhiên, mỗi lần bạn khởi động lại quy trình công nhân của bạn, từ điển sẽ trống, nhưng bạn đang nhận được ít nhất một số lợi ích bộ nhớ đệm mà không phải đối phó với việc lưu giữ thông tin bộ nhớ đệm ở đâu đó.

Bạn có thể hỗ trợ phương pháp này bằng cách tách mối quan tâm của "Tạo hình ảnh" và "Gửi hình ảnh qua HTTP" vào các lớp khác nhau. Ngay bây giờ bạn đang làm hai điều rất khác nhau ở cùng một nơi.

Tôi biết điều này nghe có vẻ hơi phức tạp nhưng đáng giá. Tôi vừa mới triển khai phương pháp này và tiết kiệm thời gian xử lý và sử dụng băng thông thật đáng kinh ngạc.

0

Bạn có bất kỳ phản hồi đệm nào xảy ra không? Nếu vậy, bạn có thể muốn đặt tiêu đề trước khi ghi vào luồng đầu ra. tức là thử di chuyển đường dây Response.OutputStream.Write() xuống dưới các dòng thiết lập Cache.

7

Nếu bạn có tập tin nguồn trên đĩa, bạn có thể sử dụng mã này:

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

Ngoài ra, hãy chắc chắn rằng bạn kiểm tra sử dụng IIS, không phải từ Visual Studio. ASP.Máy chủ Phát triển .NET (còn gọi là Cassini) luôn đặt Cache-Control thành riêng tư.

Xem thêm: Caching Tutorial for Web Authors and Webmasters

6

Đây là cách nó được thực hiện trong Roadkill's (một NET wiki) tập tin xử lý:

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; 

câu trả lời Thomas về IIS không cung cấp mã trạng thái là chìa khóa, mà không có nó bạn chỉ nhận được 200s mỗi lần.

Trình duyệt sẽ chỉ gửi cho bạn ngày và thời gian khi nó cho rằng tệp được sửa đổi lần cuối (không có tiêu đề), vì vậy nếu nó khác với bạn chỉ cần trả về 200. Bạn cần bình thường hóa ngày của tệp loại bỏ mili giây và đảm bảo đó là ngày UTC.

tôi đã đi cho mặc định cho 304S nếu có một giá trị Modified-Since, nhưng điều đó có thể được điều chỉnh nếu cần thiết.

Các vấn đề liên quan