こんにちは、私は砂漠の果ての狼です。
上文介绍了《C#使用CefSharp内嵌网页-并给出C#与JS的交互示例》,本文介绍CefSharp的缓存实现,先来说说添加缓存的好处:
- ページ読み込みの高速化:CefSharpキャッシュは、すでに読み込まれたページとリソースをキャッシュし、ユーザーが同じページを再度訪問したときに、ページとリソースを再ダウンロードして解析することなく、キャッシュから直接読み込むことができます。
- ネットワークトラフィックの削減:キャッシュを使用すると、すでにダウンロードされたリソースをキャッシュから直接読み取ることができ、再ダウンロードする必要がなくなるため、ネットワークトラフィックを削減できます。
- ユーザーエクスペリエンスの向上:キャッシュはページの読み込み速度を向上させるため、ユーザーエクスペリエンスが向上し、ユーザーはページやリソースにすばやくアクセスし、アプリケーションをより楽しく使用できます。
- サーバー負荷の軽減:キャッシュを使用すると、すでにダウンロードされたリソースを再生成して送信することなくキャッシュから直接読み取ることができるため、サーバー負荷を軽減できます。
- オフラインアクセス:ダウンロードしたページやリソースをキャッシュし、ユーザーがネットワークに接続していないときにキャッシュから直接読み込むことができるため、アプリケーションはオフラインアクセスをサポートできます。
要約すると、キャッシュを使用すると、アプリケーションのパフォーマンスとユーザーエクスペリエンスが向上し、ネットワークトラフィックとサーバー負荷が軽減され、オフラインアクセスが可能になり、非常に便利な機能です。
本文示例:GitHub
断网情况下,演示加载已经缓存的百度、百度翻译、Dotnet9首页、Dotnet9关于4个页面:

次にキャッシュの実現方法を説明する.
1. デフォルトのキャッシュ実装
CefSharpのデフォルトのキャッシュ実装はChromiumのキャッシュメカニズムに基づいている。Chromiumは、メモリキャッシュとディスクキャッシュの2種類のキャッシュを使用します。
1.1.メモリーキャッシュ
メモリキャッシュはLRU(Recently Used)アルゴリズムに基づいたキャッシュで、最近訪問したページやリソースをキャッシュします。メモリキャッシュのサイズには制限があり、キャッシュが最大サイズに達すると、最も最近使用されたページとリソースは削除されます。
メモリキャッシュはCefSharp.WPFのAPIでは設定できません。具体的には、Chromiumはメモリ内にLRU(Least Recently Used)キャッシュを維持し、最近訪問したWebページのデータを保存します。キャッシュ容量が不足すると、ChromiumはLRUアルゴリズムに基づいて最近使用されたキャッシュデータを自動的に消去し、新しいデータのためのスペースを確保します。
CefSharp.WPFでは、Cef.GetGlobalRequestContext.ClearCacheAsyncメソッドを呼び出すことで、メモリ·キャッシュからデータを消去できます。このメソッドは、メモリキャッシュとディスクキャッシュの両方を含むすべてのキャッシュデータを消去しますメモリキャッシュのクリアだけが必要なは、Cef. Get. CefCType.メソッドを呼び出します。
メモリキャッシュはChromium自身によって管理されているため、サイズを直接制御することはできません。キャッシュのサイズを制御する必要がある場合は、ディスクキャッシュのサイズを設定することで、メモリキャッシュのサイズを間接的に制御できます。
1.2.ディスク·キャッシュ
ディスクキャッシュはファイルシステムベースのキャッシュであり、ダウンロードしたページとリソースをキャッシュします。ディスクキャッシュのサイズにも制限があり、キャッシュが最大サイズに達すると、最も古いページとリソースが削除されます。
CefSharp.WPFのディスクキャッシュは、CefSettingsのCachePathプロパティを設定することで実現される。具体的には、以下のコードでディスクキャッシュのパスを設定できます。
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// CachePath需要为绝对路径
var settings = new CefSettings
{
CachePath = $"{AppDomain.CurrentDomain.BaseDirectory}DefaultCaches"
};
Cef.Initialize(settings);
}
}
キャッシュ·ディレクトリの構造は次のとおりです。

ここで、CachePathプロパティは、ディスク·キャッシュのパス絶対パスを指定します。このプロパティを設定しない場合、Chromiumはキャッシュされたデータをデフォルトパス(通常はユーザーディレクトリのAppData\Local\CefSharpディレクトリ)に保存する。
ディスクキャッシュのサイズはChromium自身によって制御されており、キャッシュされたデータがディスクに保存される最大容量はCacheControllerのSetCacheLimitメソッドを設定することで制御できます。このメソッドは、キャッシュされるデータの最大サイズ(バイト単位)を表すlong型の引数を取ります。たとえば、次のコードでは、ディスク·キャッシュの最大サイズを100MBに設定します。
var cacheController = Cef.GetGlobalRequestContext().CacheController;
cacheController.SetCacheLimit(100 * 1024 * 1024); // 100MB
ChromiumはLRUアルゴリズムに基づいて最近使用されたキャッシュデータを自動的に消去し、新しいデータのためのスペースを確保することに注意してください。そのため、キャッシュサイズを設定しても、すべてのデータがキャッシュされる保証はありません。ディスクキャッシュ内のデータをクリアする必要があるは、Cef. Get. CAsyncメソッドを呼び出します。
デフォルトのキャッシュウェブマスターはあまり研究しておらず、上記のコードとChatGPT検索を通じて説明は、カスタムキャッシュの実装を見てみましょう、デフォルトのキャッシュは単なる紹介です。
2. カスタム·キャッシュ
这是本文介绍的重点,相对于默认缓存,自定义缓存有以下好处:
- 柔軟性の向上:アプリケーションのニーズに応じてキャッシュポリシーとキャッシュサイズを柔軟に設定できるため、アプリケーションのニーズをより良く満たすことができます。
- パフォーマンスの向上アプリケーションのニーズや特定のシナリオに合わせて構成し、パフォーマンスを向上させることができます。デフォルトのキャッシュは特定のシナリオやアプリケーションのニーズには適していない場合がありますが、カスタムキャッシュはニーズに合わせて調整してパフォーマンスを向上させることができます。
- セキュリティの向上:キャッシュに保存されているコンテンツとキャッシュのライフサイクルを制御できるため、ユーザーのプライバシーとセキュリティがより良く保護されます。
- 制御性の向上:キャッシュのパージ時間やパージポリシーなど、キャッシュの動作をより良く制御し、キャッシュをより良く管理できます。
- 互換性の向上:さまざまなブラウザやデバイスにより適しています。デフォルトのキャッシュでは十分な互換性が得られない場合があります。カスタムキャッシュはニーズに合わせて調整して互換性を向上させることができます。
- より効率的:システムリソースをより良く利用できます。例えば、キャッシュを格納するために高速なストレージデバイスを使用することができ、キャッシュの読み書き速度が向上します。
总结:自定义缓存可以提供更好的性能、响应性、安全性和兼容性,从而提高应用程序的质量和用户体验,人话就是更好的操控。
2.1.コードの実装
** 前に追加したデフォルトのキャッシュコードにコメントします。**
2.1.1.登録リソース要求インターセプトハンドラ
首先在使用ChromiumWebBrowser控件的后台代码里,注册请求拦截处理程序,CefBrowser是控件名,CefRequestHandlerc是处理程序:
public TestCefCacheView()
{
InitializeComponent();
var handler = new CefRequestHandlerc();
CefBrowser.RequestHandler = handler;
}
2.1.2.要求インターセプトハンドラ
CefSharp里的IRequestHandler是一个接口,用于处理浏览器发出的请求。它定义了一些方法,可以在请求被发送到服务器之前或之后对请求进行处理。
IRequestHandler的实现类可以用于以下几个方面:
リクエストの傍受:OnBeforeBrowseメソッドを実装することで、リクエストを傍受し、ブラウザの動作を制御できます。例えば、リクエストのURLは、リクエストがサーバーに送信される前にチェックされ、要件に適合していない場合はキャンセルされたり、別のページにリダイレクトされたりすることができます。
リクエストの変更:OnBeforeResourceLoadメソッドを実装してリクエストを変更できます。たとえば、カスタムHTTPヘッダー情報を追加したり、リクエストのURLを変更したりできます。
レスポンスの処理:OnResourceResponseメソッドを実装することで、サーバーから返されたレスポンスを処理できます。例えば、レスポンスのステータスコードとコンテンツをチェックしてページの読み込みを続行するかどうかを決定できます。
キャッシュ制御:OnQuotaRequestメソッドを実装することで、キャッシュのサイズとパージポリシーを制御でき、キャッシュの使用を最適化できます。
总之,IRequestHandler的实现类可以用于控制浏览器的行为,优化网络请求和缓存的使用,从而提高应用程序的性能和用户体验。
我们不直接实现接口IRequestHandler,而是继承它的一个默认实现类RequestHandler,这可以简化我们的开发,毕竟实现接口要列出一系列接口方法。
我们重载方法GetResourceRequestHandler, 在这个方法里返回CefResourceRequestHandler实例,页面中资源请求时会调用此方法:
using CefSharp;
using CefSharp.Handler;
namespace WpfWithCefSharpCacheDemo.Caches;
internal class CefRequestHandlerc : RequestHandler
{
protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame,
IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling)
{
// 一个请求用一个CefResourceRequestHandler
return new CefResourceRequestHandler();
}
}
2.1.3.リソース要求インターセプトプログラム
在CefSharp中,IResourceRequestHandler接口是用于处理资源请求的,它可以拦截浏览器发出的资源请求,例如图片、CSS、JavaScript等,从而实现对资源请求的控制和优化。
具体来说,IResourceRequestHandler接口定义了一些方法,例如OnBeforeResourceLoad、OnResourceResponse等方法,这些方法可以用于拦截请求、修改请求、处理响应等方面。例如:
OnBeforeResourceLoad:ブラウザがリソースを要求する前に呼び出され、カスタムHTTPヘッダー情報の追加やリクエストのURLの変更など、リクエストを変更するために使用できます。
OnResourceResponse:ブラウザがサーバーから返されたレスポンスを受信した後に呼び出され、レスポンスのステータスコードやコンテンツをチェックしてページの読み込みを続行するかどうかを決定するなど、レスポンスを処理するために使用できます。
OnResourceLoadComplete:リソースのロードが完了した後に呼び出され、ローカルキャッシュへのリソースの保存など、リソースのロードが完了した後のアクションを処理するために使用できます。
通过实现IResourceRequestHandler接口,可以对资源请求进行拦截和优化,从而提高应用程序的性能和用户体验。
这里我们也不直接实现IResourceRequestHandler接口,我们定义CefResourceRequestHandler类,继承该接口的默认实现类ResourceRequestHandler。
在下面的CefResourceRequestHandler类中:
GetResourceHandler方法:处理资源是否需要缓存,返回null不缓存,返回CefResourceHandler表示需要缓存,在这个类中做跨域处理。GetResourceResponseFilter方法:注册资源缓存的操作类,即资源下载的实现。OnBeforeResourceLoad方法:在这个方法里,我们可以实现给页面传递header参数。
using System.Collections.Specialized;
using CefSharp;
using CefSharp.Handler;
namespace WpfWithCefSharpCacheDemo.Caches;
internal class CefResourceRequestHandler : ResourceRequestHandler
{
private string _localCacheFilePath;
private bool IsLocalCacheFileExist => System.IO.File.Exists(_localCacheFilePath);
protected override IResourceHandler? GetResourceHandler(IWebBrowser chromiumWebBrowser, IBrowser browser,
IFrame frame, IRequest request)
{
try
{
_localCacheFilePath = CacheFileHelper.CalculateResourceFileName(request.Url, request.ResourceType);
if (string.IsNullOrWhiteSpace(_localCacheFilePath))
{
return null;
}
}
catch
{
return null;
}
if (!IsLocalCacheFileExist)
{
return null;
}
return new CefResourceHandler(_localCacheFilePath);
}
protected override IResponseFilter? GetResourceResponseFilter(IWebBrowser chromiumWebBrowser, IBrowser browser,
IFrame frame,
IRequest request, IResponse response)
{
return IsLocalCacheFileExist ? null : new CefResponseFilter { LocalCacheFilePath = _localCacheFilePath };
}
protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser,
IFrame frame, IRequest request,
IRequestCallback callback)
{
var headers = new NameValueCollection(request.Headers);
headers["Authorization"] = "Bearer xxxxxx.xxxxx.xxx";
request.Headers = headers;
return CefReturnValue.Continue;
}
}
2.1.4. CefResourceHandler
在CefSharp中,IResourceHandler接口是用于处理资源的,它可以拦截浏览器发出的资源请求,并返回自定义的资源内容,从而实现对资源的控制和优化。
具体来说,IResourceHandler接口定义了一些方法,例如ProcessRequest、GetResponseHeaders、ReadResponse等方法,这些方法可以用于处理资源请求、获取响应头信息、读取响应内容等方面。例如:
ProcessRequest:ブラウザがリソースを要求したときに呼び出され、ローカルキャッシュからリソースコンテンツを読み込んだり、ネットワークからリソースコンテンツをダウンロードしたりするなど、リソースリクエストを処理するために使用できます。
GetResponseHeaders:ブラウザがリソースを要求するときに呼び出され、レスポンスのMIMEタイプの設定、キャッシュポリシーなどのレスポンスヘッダー情報を取得するために使用できます。
ReadResponse:ブラウザがリソースを要求したときに呼び出され、ローカルキャッシュからリソースを読み込んだり、ネットワークからリソースをダウンロードしたりするなど、レスポンスの内容を読み取るために使用できます。
IResourceHandlerインターフェイスを実装することで、ローカルキャッシュからリソースコンテンツを読み込むなど、リソースのカスタマイズ処理を行うことができ、アプリケーションのパフォーマンスとユーザーエクスペリエンスを向上させることができます。
这里我们也不直接实现IResourceHandler接口,我们定义CefResourceHandler类,继承该接口的默认实现类ResourceHandler。
在CefResourceHandler的构造函数里只处理跨域问题,其他需求可通过上面接口的方法查找资料处理即可:
using CefSharp;
using System.IO;
namespace WpfWithCefSharpCacheDemo.Caches;
internal class CefResourceHandler : ResourceHandler
{
public CefResourceHandler(string filePath, string mimeType = null, bool autoDisposeStream = false,
string charset = null) : base()
{
if (string.IsNullOrWhiteSpace(mimeType))
{
var fileExtension = Path.GetExtension(filePath);
mimeType = Cef.GetMimeType(fileExtension);
mimeType = mimeType ?? DefaultMimeType;
}
var stream = File.OpenRead(filePath);
StatusCode = 200;
StatusText = "OK";
MimeType = mimeType;
Stream = stream;
AutoDisposeStream = autoDisposeStream;
Charset = charset;
Headers.Add("Access-Control-Allow-Origin", "*");
}
}
2.1.5. CefResponseFilter
在CefSharp中,IResponseFilter接口是用于过滤响应内容的,它可以拦截浏览器接收到的响应内容,并对其进行修改或者过滤,从而实现对响应内容的控制和优化。
具体来说,IResponseFilter接口定义了一些方法,例如InitFilter、Filter、GetSize等方法,这些方法可以用于初始化过滤器、过滤响应内容、获取过滤后的响应内容大小等方面。例如:
InitFilter:ブラウザがレスポンスの内容を受信したときに呼び出され、フィルタの状態設定やレスポンスヘッダー情報の取得など、フィルタの初期化に使用できます。
フィルタ:ブラウザがレスポンスコンテンツを受信したときに呼び出され、レスポンスコンテンツの変更や削除など、レスポンスコンテンツのフィルタリングに使用できます。
GetSize:ブラウザがレスポンスコンテンツを受信したときに呼び出され、フィルタリングされたレスポンスコンテンツのサイズを取得するために使用できます。例えば、レスポンスコンテンツの圧縮率を計算するために使用できます。
站长使用的CefSharp.Wpf的89.0.170.0版本中的IResponseFilter接口没有GetSize方法。在该版本中,IResponseFilter接口只定义了两个方法:InitFilter和Filter。
如果在该版本中您需要获取过滤后的响应内容大小,可以考虑在Filter方法中自行计算。例如,在Filter方法中,您可以将过滤后的响应内容写入一个缓冲区,并记录缓冲区的大小,最后返回过滤后的响应内容和缓冲区的大小。
public class MyResponseFilter : IResponseFilter
{
private MemoryStream outputStream = new MemoryStream();
public void Dispose()
{
outputStream.Dispose();
}
public bool InitFilter()
{
return true;
}
public FilterStatus Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten)
{
dataInRead = 0;
dataOutWritten = 0;
byte[] buffer = new byte[4096];
int bytesRead = 0;
do
{
bytesRead = dataIn.Read(buffer, 0, buffer.Length);
if (bytesRead > 0)
{
outputStream.Write(buffer, 0, bytesRead);
}
} while (bytesRead > 0);
byte[] outputBytes = outputStream.ToArray();
dataOut.Write(outputBytes, 0, outputBytes.Length);
dataInRead = outputBytes.Length;
dataOutWritten = outputBytes.Length;
return FilterStatus.Done;
}
public int GetResponseFilterBufferSize()
{
return 0;
}
public int GetResponseFilterDelay()
{
return 0;
}
}
在上述示例代码中,我们在Filter方法中将过滤后的响应内容写入了一个MemoryStream对象中,并记录了缓冲区的大小。最后,我们在Filter方法的返回值中返回了过滤后的响应内容和缓冲区的大小。
总结,通过实现IResponseFilter接口,可以对响应内容进行自定义处理,例如对响应内容进行压缩、加密等操作,从而提高应用程序的性能和安全性。
本文示例这里定义类CefResponseFilter直接实现接口处理文件缓存实际操作类,即资源下载实现:
using CefSharp;
using System.IO;
namespace WpfWithCefSharpCacheDemo.Caches;
internal class CefResponseFilter : IResponseFilter
{
public string LocalCacheFilePath { get; set; }
private const int BUFFER_LENGTH = 1024;
private bool isFailCacheFile;
public FilterStatus Filter(Stream? dataIn, out long dataInRead, Stream? dataOut, out long dataOutWritten)
{
dataInRead = 0;
dataOutWritten = 0;
if (dataIn == null)
{
return FilterStatus.NeedMoreData;
}
var length = dataIn.Length;
var data = new byte[BUFFER_LENGTH];
var count = dataIn.Read(data, 0, BUFFER_LENGTH);
dataInRead = count;
dataOutWritten = count;
dataOut?.Write(data, 0, count);
try
{
CacheFile(data, count);
}
catch
{
// ignored
}
return length == dataIn.Position ? FilterStatus.Done : FilterStatus.NeedMoreData;
}
public bool InitFilter()
{
try
{
var dirPath = Path.GetDirectoryName(LocalCacheFilePath);
if (!string.IsNullOrWhiteSpace(dirPath) && !Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
}
catch
{
// ignored
}
return true;
}
public void Dispose()
{
}
private void CacheFile(byte[] data, int count)
{
if (isFailCacheFile)
{
return;
}
try
{
if (!File.Exists(LocalCacheFilePath))
{
using var fs = File.Create(LocalCacheFilePath);
fs.Write(data, 0, count);
}
else
{
using var fs = File.Open(LocalCacheFilePath, FileMode.Append);
fs.Write(data,0,count);
}
}
catch
{
isFailCacheFile = true;
File.Delete(LocalCacheFilePath);
}
}
}
2.1.6. CacheFileHelper
ページのajaxインターフェイスのキャッシュホワイトリスト、キャッシュファイルパス仕様などを管理するキャッシュファイルヘルプクラス
using CefSharp;
using System;
using System.Collections.Generic;
using System.IO;
namespace WpfWithCefSharpCacheDemo.Caches;
internal static class CacheFileHelper
{
private const string DEV_TOOLS_SCHEME = "devtools";
private const string DEFAULT_INDEX_FILE = "index.html";
private static HashSet<string> needInterceptedAjaxInterfaces = new();
private static string CachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "caches");
public static void AddInterceptedAjaxInterfaces(string url)
{
if (needInterceptedAjaxInterfaces.Contains(url))
{
return;
}
needInterceptedAjaxInterfaces.Add(url);
}
private static bool IsNeedInterceptedAjaxInterface(string url, ResourceType resourceType)
{
var uri = new Uri(url);
if (DEV_TOOLS_SCHEME == url)
{
return false;
}
if (ResourceType.Xhr == resourceType && !needInterceptedAjaxInterfaces.Contains(url))
{
return false;
}
return true;
}
public static string? CalculateResourceFileName(string url, ResourceType resourceType)
{
if (!IsNeedInterceptedAjaxInterface(url, resourceType))
{
return default;
}
var uri = new Uri(url);
var urlPath = uri.LocalPath;
if (urlPath.StartsWith("/"))
{
urlPath = urlPath.Substring(1);
}
var subFilePath = urlPath;
if (ResourceType.MainFrame == resourceType || string.IsNullOrWhiteSpace(urlPath))
{
subFilePath = Path.Combine(urlPath, DEFAULT_INDEX_FILE);
}
var hostCachePath = Path.Combine(CachePath, uri.Host);
var fullFilePath = Path.Combine(hostCachePath, subFilePath);
return fullFilePath;
}
}
カスタムキャッシュのサブディレクトリは、リソースのドメイン名Hostにディレクトリ名として作成されます。

打开缓存的dotnet9.com目录,通过查看目录结构和程序发布目录基本一致,这更适合人看了,是不?

2.2.可能な問題は
第一点,站长目前遇到的问题,后面4点由Token AI提供解释。
2.2.1. Query Stringを使用したキャッシュされたリソースURLのサポートが悪い
QueryStringクエリ·パラメータのパイロット作業の代わりに、Routeルーティング方式を推奨します。https//dotnet9.com/albums/wpfは、https//dotnet9.com/albumsの代わりに使用されます。slug=wpfの方式は,ウェブマスターがQueryStringのキャッシュ方式を再検討する余裕がある.
リソースにQuery Stringが含まれている場合は、そのリソースのキャッシュを解放し、ネットワーク経由で直接リクエストします。
2.2.2.キャッシュ·コンシステンシの問題
カスタムキャッシュがキャッシュの一貫性を適切に処理しないと、ブラウザが古いコンテンツや一貫性のないコンテンツを表示する可能性があります。例えば、ウェブページがキャッシュされているが、そのページがサーバー上で更新されている場合、カスタムキャッシュがキャッシュの一貫性を適切に処理しないと、ブラウザが古いウェブページコンテンツを表示する可能性があります。
2.2.3.キャッシュスペースの問題
カスタムキャッシュがキャッシュ領域を適切に管理しないと、ブラウザがメモリやディスク領域を過剰に使用する可能性があります。たとえば、カスタムキャッシュが大量のデータをキャッシュしているが、期限切れのデータをクリーンアップしたり、キャッシュサイズを制限したりしないと、ブラウザがメモリやディスク領域を過剰に消費する可能性があります。
2.2.4.キャッシュ·パフォーマンスの問題
カスタムキャッシュがキャッシュパフォーマンスを正しく処理しないと、ブラウザのパフォーマンスが低下する可能性があります。たとえば、カスタムキャッシュがキャッシュの読み取りと書き込みを適切に処理しないと、ブラウザの応答が遅くなる可能性があります。
2.2.5.キャッシュセキュリティ問題
カスタムキャッシュがキャッシュセキュリティを適切に処理しないと、ブラウザのセキュリティが損なわれる可能性があります。たとえば、カスタムキャッシュが機密データをキャッシュしているが、キャッシュの暗号化と復号化を適切に処理しないと、機密データが漏洩する可能性があります。
したがって、キャッシュをカスタマイズする際には、ブラウザの正常な動作とセキュリティを確保するために、キャッシュの一貫性、キャッシュスペース、キャッシュパフォーマンス、キャッシュセキュリティなどの問題に対処するよう注意する必要があります。
参考文献より:
微信技術交流グループ:微信(codewf)コメント“グループに入る”を追加
QQ技術交流グループ:77 199230 0。
