web-dev-qa-db-ja.com

ASP.NET MVC:アクションからブラウザーのキャッシュイメージを作成する

Fileを返し、引数(ID)が1つしかないアクションメソッドがあります。

例えば.

public ActionResult Icon(long id)
{
    return File(Server.MapPath("~/Content/Images/image" + id + ".png"), "image/png");
}

初めてアクセスしたときにブラウザがこの画像を自動的にキャッシュするようにして、次回はすべてのデータをダウンロードする必要がないようにします。

私はOutputCacheAttributeなどを使用して、応答にヘッダーを手動で設定してみました。つまり:

[OutputCache(Duration = 360000)]

または

Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetExpires(Cache.NoAbsoluteExpiration); 

しかし、ブラウザでF5を押すたびに画像が読み込まれます(ChromeおよびIEで試してみます)(画像を変更するとブラウザでも変更されます)。

HTTP応答に、明らかに機能するはずのヘッダーがいくつかあることがわかります。

Cache-Control:public、max-age = 360000

コンテンツの長さ:39317

Content-Type:image/png

日付:2012年1月31日火曜日23:20:57 GMT

有効期限:2012年2月5日、日03:20:56 GMT

最終更新:2012年1月31日火曜日23:20:56 GMT

しかし、リクエストヘッダーにはこれがあります:

プラグマ:キャッシュなし

これを行う方法について何か考えはありますか?

どうもありがとう

29
willvv

最初に注意すべきことは、Chrome、Safari、またはIEでF5(更新)を押すと、画像がブラウザーにキャッシュされていても、再度要求されることです。

画像を再度ダウンロードする必要がないことをブラウザに伝えるには、以下のように、コンテンツのない304応答を返す必要があります。

Response.StatusCode = 304;
Response.StatusDescription = "Not Modified";
Response.AddHeader("Content-Length", "0");

ただし、304応答を返す前に、If-Modified-Sinceリクエストヘッダーを確認する必要があります。そのため、If-Modified-Sinceの日付を画像リソースの変更日と照合する必要があります(ファイルからのものか、データベースに保存されているかなど)。ファイルが変更されていない場合は304を返し、それ以外の場合は画像(リソース)を返します。

この機能を実装する良い例をいくつか示します(これらはHttpHandler用ですが、同じ原則をMVCアクションメソッドに適用できます)。

20
brodie

このコードを試してください、私にとってはうまくいきます

            HttpContext.Response.Cache.SetCacheability(HttpCacheability.Public);
            HttpContext.Response.Cache.SetMaxAge(new TimeSpan(1, 0, 0));

            Entities.Image objImage = // get your Image form your database

            string rawIfModifiedSince = HttpContext.Request.Headers.Get("If-Modified-Since");
            if (string.IsNullOrEmpty(rawIfModifiedSince))
            {
                // Set Last Modified time
                HttpContext.Response.Cache.SetLastModified(objImage.ModifiedDate);
            }
            else
            {
                DateTime ifModifiedSince = DateTime.Parse(rawIfModifiedSince);


                // HTTP does not provide milliseconds, so remove it from the comparison
                if (objImage.ModifiedDate.AddMilliseconds(
                            -objImage.ModifiedDate.Millisecond) == ifModifiedSince)
                {
                    // The requested file has not changed
                    HttpContext.Response.StatusCode = 304;
                    return Content(string.Empty);
                }
            }

            return File(objImage.File, objImage.ContentType);
13
user2273400

私は@ user2273400のソリューションを使用しましたが、機能するため、完全なソリューションを投稿しています。

これは私のアクションと一時的な支援方法を持つ私のコントローラーです:

using System;
using System.Web;
using System.Web.Mvc;
using CIMETY_WelcomePage.Models;

namespace CIMETY_WelcomePage.Controllers
{
    public class FileController : Controller
    {

        public ActionResult Photo(int userId)
        {
            HttpContext.Response.Cache.SetCacheability(HttpCacheability.Public);
            HttpContext.Response.Cache.SetMaxAge(new TimeSpan(1, 0, 0));

            FileModel model = GetUserPhoto(userId);


            string rawIfModifiedSince = HttpContext.Request.Headers.Get("If-Modified-Since");
            if (string.IsNullOrEmpty(rawIfModifiedSince))
            {
                // Set Last Modified time
                HttpContext.Response.Cache.SetLastModified(model.FileInfo.LastWriteTime);
            }
            else
            {
                DateTime ifModifiedSince = DateTime.Parse(rawIfModifiedSince);


                // HTTP does not provide milliseconds, so remove it from the comparison
                if (TrimMilliseconds(model.FileInfo.LastWriteTime.AddMilliseconds) <= ifModifiedSince)
                {
                    // The requested file has not changed
                    HttpContext.Response.StatusCode = 304;
                    return Content(string.Empty);
                }
            }

            return File(model.File, model.ContentType);
        }

        public FileModel GetUserPhoto(int userId)
        {
            string filepath = HttpContext.Current.Server.MapPath("~/Photos/635509890038594486.jpg");
            //string filepath = frontAdapter.GetUserPhotoPath(userId);

            FileModel model = new FileModel();
            model.File = System.IO.File.ReadAllBytes(filepath);
            model.FileInfo = new System.IO.FileInfo(filepath);
            model.ContentType = MimeMapping.GetMimeMapping(filepath);

            return model;
        }

    private DateTime TrimMilliseconds(DateTime dt)
    {
        return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, 0);
    }

    }
}

次に、Modelクラス:

public class FileModel
{
    public byte[] File { get; set; }
    public FileInfo FileInfo { get; set; }
    public String ContentType { get; set; }
}

そして、私はそれをどのように使用していますか:

<img src="@Url.Action("Photo", "File", new { userId = 15 })" />
1
prespic

ちょうど私が明らかにしたことをあなたに知らせるために、私がどのように伝えることができるかについての説明または詳細情報が欲しいです。

これ:Response.Cache.SetCacheability(HttpCacheability.Public); Response.Cache.SetExpires(Cache.NoAbsoluteExpiration);

実際に画像をキャッシュします...

これを証明するには... debugging ...でテストし、イメージコントローラーにブレークポイントを設定します。F5を2回実行しても、一度はヒットしないことがわかります。回...しかし、私にとって奇妙なのは、200応答がまだ返されることです。

それらの行を外すと、ブレークポイントに再び到達し始めることがわかります。

私の質問:200の応答がまだ返される場合に、このイメージがキャッシュからサーバーであることを確認する正しい方法は何ですか?.

これはIIS express 8.でテストしていました。組み込みのIIS for Visual studioはGdではありません。

0
Calvin Nel