问题描述
我是从使用IHttpHandler的数据库提供了一个形象。有关code是在这里:
公共无效的ProcessRequest(HttpContext的背景下)
{
context.Response.ContentType =图像/ JPEG;
诠释imageID;
如果(int.TryParse(context.Request.QueryString [ID],出imageID))
{
变种照片=新CoasterPhoto(imageID);
如果(photo.CoasterPhotoID == 0)
context.Response.Status code = 404;
其他
{
字节[]为imageData = GetImageData(照片);
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);
}
}
其他
context.Response.Status code = 404;
}
问题是,浏览器将不缓存图像,presumably因为我不指示在响应头正确的事情。呼吁为HttpCachePolicy属性方法的部分是什么,我以为会强制浏览器扶住形象,但事实并非如此。我认为正确的事情是处理程序返回304状态code,无图像,对不对?我如何做到这一点使用的IHttpHandler?
编辑:
每最好的答案,我得到这个code运行,它彻底解决了这个问题。是的,它需要一些重构,但它通常表明我是什么了。相关部分:
如果(!String.IsNullOrEmpty(context.Request.Headers [如果-Modified-Since的]))
{
CultureInfo的提供商= CultureInfo.InvariantCulture;
VAR的lastmod = DateTime.ParseExact(context.Request.Headers [如果-Modified-Since的],R,供应商).ToLocalTime();
如果(==的lastmod photo.SubmitDate)
{
context.Response.Status code = 304;
context.Response.StatusDescription =不修改;
返回;
}
}
字节[]为imageData = GetImageData(照片);
context.Response.OutputStream.Write(的imageData,0,imageData.Length);
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetLastModified(photo.SubmitDate);
据我所知,您负责发送304未修改,这意味着我不知道任何事情在.NET Framework,做它你在你的这种使用情况下发送动态的图像数据。你将要做的(伪code):
- 检查了If-Modified-Since的请求头和解析出日期(如果存在的话)。
- 比较它原始图像(动态生成的)图像的最后修改日期。跟踪这可能是解决这个问题的最复杂的部分。在你目前的情况,你重新创建在每次请求的形象;您不想这样做,除非你绝对必须的。
- 如果浏览器拥有文件的日期较新或等于你有什么形象,发送一个304未修改。
- 否则,继续使用当前的实施
有一个简单的方法来跟踪你的最终最后修改时间是在文件系统缓存新生成的图像,并保持在内存中的字典周围的图像ID映射到包含磁盘上的文件名和最后修改一个struct日期。使用Response.WriteFile从磁盘传送数据。当然,每次重新启动您的工作进程时,将字典是空的,但你至少获得一些好处缓存,而不必处理与地方持续缓存信息。
您可以通过分离的图像一代的关注和通过HTTP发送图像为不同的类支持这种做法。现在你正在做在同一个地方两个非常不同的事情。
我知道这听起来可能有点复杂,但它是值得的。我最近刚刚实施的这一做法,并在处理时间和带宽使用储蓄是令人难以置信的。
I'm serving up an image from a database using an IHttpHandler. The relevant code is here:
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;
}
The problem is that the browser won't cache the image, presumably because I'm not indicating the right thing in the response headers. The part calling methods on the HttpCachePolicy property is what I thought would force the browser to hold on to the image, but it doesn't. I think the "right" thing is for the handler to return a 304 status code without an image, right? How do I achieve that using IHttpHandler?
EDIT:
Per the best answer, I got this code running and it completely solves the problem. Yes, it needs some refactoring, but it generally demonstrates what I was after. The relevant parts:
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);
AFAIK, you are responsible for sending 304 Not Modified, meaning I am not aware of anything in the .Net framework that does it for you in this use case of you sending "dynamic" image data. What you will have to do (in pseudo code):
- Check for the If-Modified-Since header in the request and parse out the date (if it exists).
- Compare it to the last modification date of your original image (dynamically generated) image. Tracking this is probably the most complex part of the solution to this problem. In your current situation, you are re-creating the image on every request; you don't want to do that unless you absolutely have to.
- If the date of the file the browser has is newer or equal to what you have for the image, send a 304 Not Modified.
- Otherwise, continue with your current implementation
A simple way to track last modified times on your end is to cache newly generated images on the file system and keep an in-memory dictionary around that maps the image ID to a struct containing the file name on disk and the last modification date. Use Response.WriteFile to send the data from disk. Of course, every time you restart your worker process, the dictionary would be empty, but you're getting at least some caching benefit without having to deal with persisting caching information somewhere.
You can support this approach by separating the concerns of "Image Generation" and "Sending Images over HTTP" into different classes. Right now you're doing two very different things in the same place.
I know this may sound a little complex, but it's worth it. I just recently implemented this approach and the savings in processing time and bandwidth usage were incredible.
这篇关于从的HttpHandler不会在浏览器缓存图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!