21

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

4 に答える 4

24

私の知る限り、あなたは 304 Not Modified を送信する責任があります。つまり、「動的な」画像データを送信するこのユースケースで、.Net フレームワークでそれを行うものは何も知りません。あなたがしなければならないこと(疑似コードで):

  • リクエストの If-Modified-Since ヘッダーを確認し、日付を解析します (存在する場合)。
  • 元の画像 (動的に生成された) 画像の最終更新日と比較します。これを追跡することは、おそらくこの問題の解決策の中で最も複雑な部分です。現在の状況では、リクエストごとにイメージを再作成しています。絶対に必要でない限り、それをしたくありません
  • ブラウザが持っているファイルの日付が画像の日付よりも新しいか等しい場合は、304 Not Modified を送信します。
  • それ以外の場合は、現在の実装を続行します

最後に変更された時刻を追跡する簡単な方法は、新しく生成されたイメージをファイル システムにキャッシュし、ディスク上のファイル名と最終変更日を含む構造体にイメージ ID をマップするメモリ内ディクショナリを保持することです。Response.WriteFile を使用して、ディスクからデータを送信します。もちろん、ワーカー プロセスを再起動するたびに、ディクショナリは空になりますが、永続的なキャッシュ情報をどこかで処理しなくても、少なくともキャッシュのメリットがいくらか得られます。

「画像の生成」と「HTTP 経由での画像の送信」の問題を異なるクラスに分離することで、このアプローチをサポートできます。現在、あなたは同じ場所で 2 つの非常に異なることを行っています。

少し複雑に聞こえるかもしれませんが、それだけの価値はあります。最近このアプローチを実装したところ、処理時間と帯域幅の使用量が大幅に削減されました。

于 2009-06-15T01:17:36.140 に答える
7

ディスクにソース ファイルがある場合は、次のコードを使用できます。

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

また、Visual Studio からではなく、IIS を使用してテストするようにしてください。ASP.NET 開発サーバー (別名 Cassini) は常に Cache-Control を非公開に設定します。

関連項目: Web 作成者および Web マスター向けのキャッシング チュートリアル

于 2009-06-15T04:52:34.540 に答える
6

これは、Roadkill の(.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 がステータス コードを提供していないことに関する Thomas の回答が重要です。

ブラウザは、ファイルが最後に変更されたと思われる日付と時刻を送信するだけなので (ヘッダーはまったくありません)、それが異なる場合は 200 を返すだけです。ミリ秒を削除して確実にファイルの日付を正規化する必要があります。それはUTCの日付です。

有効な変更されたので、私はデフォルトで 304 を使用しましたが、必要に応じて調整できます。

于 2013-03-22T22:09:41.310 に答える
0

応答バッファリングが発生していますか? その場合は、出力ストリームに書き込む前にヘッダーを設定することをお勧めします。つまり、Response.OutputStream.Write()行をキャッシュ設定行の下に移動してみてください。

于 2009-06-15T01:01:02.903 に答える