Hello everyone, I am a wolf at the end of the desert.
上文介绍了《C#使用CefSharp内嵌网页-并给出C#与JS的交互示例》,本文介绍CefSharp的缓存实现,先来说说添加缓存的好处:
- Improve page loading acceleration: CefSharp Cache can cache pages and resources that have already been loaded. When users access the same page again, they can directly load it from the cache without having to download and parse pages and resources again, thereby accelerating page loading.
- Reduce network traffic: Using caching can reduce network traffic because resources that have already been downloaded can be read directly from the cache without having to download again.
- Improve user experience: Because caching can increase page loading speed, it can improve the user experience, and users can access pages and resources faster, making it more enjoyable to use the application.
- Reduce server load: Using caching reduces server load because resources that have been downloaded can be read directly from the cache without needing to be regenerated and sent.
- Offline access: You can enable the application to support offline access because it can cache pages and resources that have been downloaded, and when users do not have a network connection, pages and resources can be directly loaded from the cache.
In summary, using caching can improve application performance and user experience, reduce network traffic and server load, and support offline access, which is a very useful feature.
本文示例:GitHub
断网情况下,演示加载已经缓存的百度、百度翻译、Dotnet9首页、Dotnet9关于4个页面:

Next, we will explain the implementation of caching.
1. Default cache implementation
CefSharp's default caching implementation is based on Chromium's caching mechanism. Chromium uses two types of caching: memory caching and disk caching.
1.1. memory cache
Memory cache is a cache based on the LRU (Least Recently Used) algorithm that caches recently accessed pages and resources. The size of the memory cache is limited, and when the cache reaches its maximum size, the least recently used pages and resources will be deleted.
Memory caching cannot be set through the CefSharp.WPF API. Specifically, Chromium maintains an LRU (Least Recently Used) cache in memory to store data on recently visited web pages. When there is insufficient cache space, Chromium will automatically clear the least recently used cached data based on the LRU algorithm to make room for new data.
In CefSharp.WPF, we can clear data from the memory cache by calling the Cef.GetGlobalRequestContext().ClearCacheAsync() method. This method clears all cached data, including memory cache and disk cache. If you only need to clear the memory cache, you can call the Cef.GetGlobalRequestContext().ClearCache(CefCacheType.MemoryCache) method.
It should be noted that since the memory cache is maintained by Chromium itself, we cannot directly control its size. If you need to control the cache size, you can indirectly control the size of the memory cache by setting the size of the disk cache.
1.2. disk cache
Disk cache is a file system-based cache that caches downloaded pages and resources. The size of the disk cache is also limited. When the cache reaches its maximum size, the oldest pages and resources will be deleted.
Disk caching for CefSharp.WPF is achieved by setting the CachePath property in CefSettings. Specifically, we can set the path to the disk cache by using the following code:
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);
}
}
The cache directory structure is as follows:

Among them, the CachePath attribute specifies the path to the disk cache (absolute path). If this property is not set, Chromium stores the cached data under the default path (usually the AppData\Local\CefSharp directory in the user directory).
It should be noted that the size of the disk cache is controlled by Chromium itself. We can control the maximum space for cached data to be stored on the disk by setting the SetCacheLimit method of the CacheController. This method accepts an argument of type long, which represents the maximum size of cached data in bytes. For example, the following code sets the maximum size of the disk cache to 100MB:
var cacheController = Cef.GetGlobalRequestContext().CacheController;
cacheController.SetCacheLimit(100 * 1024 * 1024); // 100MB
It should be noted that Chromium will automatically clear the least recently used cached data based on the LRU algorithm to make room for new data. Therefore, even if the cache size is set, there is no guarantee that all data will be cached. If you need to clear data from the disk cache, you can call the Cef.GetGlobalRequestContext().ClearCacheAsync() method.
There is not much research on default caching by webmasters. The above code and description are obtained through ChatGPT search. Let's look at the implementation of custom caching. The default caching is just a primer.
2. custom cache
这是本文介绍的重点,相对于默认缓存,自定义缓存有以下好处:
- More flexible: You can flexibly configure cache policies and cache sizes based on the needs of the application to better meet the needs of the application.
- Better performance: It can be configured based on application needs and specific scenarios to achieve better performance. The default cache may not be suitable for certain scenarios or the needs of your application, while a custom cache can be adjusted to your needs for better performance.
- Better security: Users 'privacy and security can be better protected because you can control what is stored in the cache and the lifetime of the cache.
- More controllable: You can better control cache behavior, such as cache cleanup time and cleanup strategy, to better manage cache.
- Better compatibility: It can be better adapted to different browsers and devices, the default cache may not provide enough compatibility, while the custom cache can be adjusted to your needs to provide better compatibility.
- More efficient: You can make better use of system resources, such as using faster storage devices to store caches, thereby increasing the read and write speed of the cache.
总结:自定义缓存可以提供更好的性能、响应性、安全性和兼容性,从而提高应用程序的质量和用户体验,人话就是更好的操控。
2.1. code implementation
** Comment on the default cache code added before. **
2.1.1. Register resource request interception handlers
首先在使用ChromiumWebBrowser控件的后台代码里,注册请求拦截处理程序,CefBrowser是控件名,CefRequestHandlerc是处理程序:
public TestCefCacheView()
{
InitializeComponent();
var handler = new CefRequestHandlerc();
CefBrowser.RequestHandler = handler;
}
2.1.2. request interception handler
CefSharp里的IRequestHandler是一个接口,用于处理浏览器发出的请求。它定义了一些方法,可以在请求被发送到服务器之前或之后对请求进行处理。
IRequestHandler的实现类可以用于以下几个方面:
Intercept requests: You can control browser behavior by implementing the OnBeforeBrowse method to intercept requests. For example, you can check the URL of the request before it is sent to the server, and if it does not meet the requirements, you can cancel the request or redirect it to another page.
Modify the request: You can modify the request by implementing the OnBeforeResourceLoad method. For example, you can add some custom HTTP header information or modify the request URL.
Processing responses: You can process the response returned by the server by implementing the OnResourceResponse method. For example, you can check the status code and content of the response to decide whether to continue loading the page.
Cache control: You can implement the OnQuotaRequest method to control the cache size and clearing strategy, thereby optimizing cache use.
总之,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. resource request interceptor
在CefSharp中,IResourceRequestHandler接口是用于处理资源请求的,它可以拦截浏览器发出的资源请求,例如图片、CSS、JavaScript等,从而实现对资源请求的控制和优化。
具体来说,IResourceRequestHandler接口定义了一些方法,例如OnBeforeResourceLoad、OnResourceResponse等方法,这些方法可以用于拦截请求、修改请求、处理响应等方面。例如:
OnBeforeResourceLoad: Called before the browser requests a resource and can be used to modify the request, such as adding some custom HTTP header information or modifying the request URL.
OnResourceResponse: Called after the browser receives the response returned by the server and can be used to process the response, such as checking the status code and content of the response to decide whether to continue loading the page.
OnResourceLoadComplete: Called after resource loading is completed and can be used to handle operations after resource loading is completed, such as saving resources to local cache.
通过实现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: Called when the browser requests a resource and can be used to process resource requests, such as reading resource content from the local cache or downloading resource content from the network.
GetResponseHeaders: Called when the browser requests a resource and can be used to obtain response header information, such as setting the response's MIME type, caching policy, etc.
ReadResponse: Called when the browser requests a resource and can be used to read the response content, such as reading the resource content from the local cache or downloading the resource content from the network.
By implementing the IResourceHandler interface, resources can be customized, such as reading resource content from local cache, thereby improving application performance and user experience.
这里我们也不直接实现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: It is called when the browser receives the response content and can be used to initialize the filter, such as setting the status of the filter, obtaining response header information, etc.
Filter: Called when the browser receives response content and can be used to filter response content, such as modifying response content, deleting response content, etc.
GetSize: Called when the browser receives the response content and can be used to obtain the size of the filtered response content, such as calculating the compression ratio of the response content.
站长使用的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
Cache file help class, which is used to manage page ajax interface cache whitelists, cache file path specifications, etc.:
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;
}
}
The subdirectory of the custom cache is created with the resource's domain name (Host) as the directory name:

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

2.2. possible problems
第一点,站长目前遇到的问题,后面4点由Token AI提供解释。
2.2.1. Poor support for cached resource URLs with QueryString
It is recommended to use Route(routing method: https://dotnet9.com/albums/wpf) instead of QueryString(trial query parameters: https://dotnet9.com/albus? slug=wpf), the webmaster will study the caching method of QueryString when he has time.
If the resource does have a QueryString, then release the cache for this resource and request it directly through the network.
2.2.2. Cache coherence issues
If a custom cache incorrectly handles cache consistency, it may cause the browser to display outdated content or inconsistent content. For example, if a web page is cached, but the web page has been updated on the server, if the custom cache does not properly handle cache consistency, it may cause the browser to display outdated web page content.
2.2.3. Cache space issues
If a custom cache does not manage cache space correctly, it may cause the browser to consume too much memory or disk space. For example, if a custom cache caches a large amount of data, but does not clean up outdated data in a timely manner or limit the size of the cache, it may cause the browser to consume too much memory or disk space.
2.2.4. Cache performance issues
If a custom cache does not properly handle cache performance, it may cause browser performance to degrade. For example, if a custom cache does not properly handle cached reads and writes, it may cause the browser to respond slowly.
2.2.5. Cache security issues
If a custom cache does not properly handle cache security, the security of the browser may be compromised. For example, if a custom cache caches sensitive data but does not properly handle the encryption and decryption of the cache, sensitive data may be leaked.
Therefore, when customizing caching, you need to pay attention to issues such as cache consistency, cache space, cache performance and cache security to ensure the normal operation and security of the browser.
Reference:
WeChat technical exchange group: Add WeChat (codewf) comment "Join the group"
QQ technical exchange group: 771992300.
