Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 来自HttpHandler的图像赢得';浏览器中的t缓存_C#_.net_Asp.net_Ihttphandler - Fatal编程技术网

C# 来自HttpHandler的图像赢得';浏览器中的t缓存

C# 来自HttpHandler的图像赢得';浏览器中的t缓存,c#,.net,asp.net,ihttphandler,C#,.net,Asp.net,Ihttphandler,我正在使用IHttpHandler从数据库中提供一个图像。相关代码如下: public void ProcessRequest(HttpContext context) { context.Response.ContentType = "image/jpeg"; int imageID; if (int.TryParse(context.Request.QueryString["id"], out imageID)) { var photo = n

我正在使用IHttpHandler从数据库中提供一个图像。相关代码如下:

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

您是否有任何响应缓冲发生?如果是这样,您可能希望在写入输出流之前设置头。i、 e.尝试将
Response.OutputStream.Write()
行向下移动到缓存设置行下方。

好的,您负责发送304 Not Modified,这意味着在.Net framework中,在您发送“动态”图像数据的这个用例中,我不知道有什么东西可以帮您做到这一点。必须执行的操作(在伪代码中):

  • 检查请求中的If-Modified-Since头并解析出日期(如果存在)
  • 将其与原始图像(动态生成)的上次修改日期进行比较。跟踪这可能是这个问题解决方案中最复杂的部分。在您当前的情况下,您正在根据每个请求重新创建图像;除非你万不得已,否则你不会想那样做
  • 如果浏览器拥有的文件日期较新或与您拥有的图像日期相同,请发送304 Not Modified
  • 否则,请继续当前的实现
跟踪上次修改时间的一种简单方法是在文件系统上缓存新生成的图像,并在其周围保留一个内存字典,将图像ID映射到包含磁盘上文件名和上次修改日期的结构。使用Response.WriteFile从磁盘发送数据。当然,每次重新启动worker进程时,字典都将是空的,但您至少可以获得一些缓存好处,而不必在某个地方处理持久缓存信息

您可以通过将“图像生成”和“通过HTTP发送图像”分为不同的类来支持这种方法。现在你在同一个地方做两件截然不同的事情


我知道这听起来有点复杂,但值得。我最近刚刚实现了这种方法,在处理时间和带宽使用方面的节省令人难以置信。

如果磁盘上有源文件,您可以使用以下代码:

context.Response.AddFileDependency(pathImageSource);
context.Response.Cache.SetETagFromFileDependencies();
context.Response.Cache.SetLastModifiedFromFileDependencies();
context.Response.Cache.SetCacheability(HttpCacheability.Public);
另外,请确保使用IIS而不是Visual Studio进行测试。ASP.NET开发服务器(又名Cassini)始终将缓存控制设置为私有

另请参见:

这是如何在(.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;
Thomas关于IIS不提供状态码的回答是关键,没有状态码,每次只能返回200秒

浏览器只需向您发送它认为文件上次被修改的日期和时间(完全没有标题),因此如果它不同,您只需返回200。您确实需要规范化文件的日期,以删除毫秒并确保它是UTC日期


如果有一个有效的修改,我将默认设置为304s,但如果需要,可以进行调整。

看起来浏览器应该将其缓存5分钟。它会缓存那么长时间吗?这就是你想要的吗?浏览器根本不缓存。此代码在每次发出请求时都会执行。完全不相关,但在使用此方法提供图像时,您是否有任何性能影响?特别是当您使用IHttpHandler在一个网页中提供多个图像时?因为我正在写入流而不是文件?请小心(lastMod==photo.SubmitDate)行,正如我所看到的,lastMod参数只能解析为秒,而不是毫秒-在我的例子中,它无法与file.creatorDateTime()进行比较,因为:)