CefSharp自定义SchemeHandler

5
我正在使用CefSharp的SchemeHandler来获取来自我的C#项目的资源,例如.css.js.png文件,使用自定义的URL,例如custom://cefsharp/assets/css/style.css
为了实现这一目的,我有两个自定义类。
第一个类是MyCustomSchemeHandlerFactory,它将处理自定义方案,看起来像这样,其中“custom”将是自定义方案:
internal class MyCustomSchemeHandlerFactory : ISchemeHandlerFactory
{
    public const string SchemeName = "custom";

    public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
    {
        return new MyCustomSchemeHandler();
    }
}

我实现的下一个类是MyCustomSchemeHandler,它将接收调用并输出响应,代码如下:
internal class MyCustomSchemeHandler : IResourceHandler
{

    private static readonly IDictionary<string, string> ResourceDictionary;

    private string mimeType;
    private MemoryStream stream;

    static MyCustomSchemeHandler()
    {
        ResourceDictionary = new Dictionary<string, string>
        {
            { "/home.html", Properties.Resources.index},
            { "/assets/css/style.css", Properties.Resources.style}
        };
    }

    public Stream Stream { get; set; }
    public int StatusCode { get; set; }
    public string StatusText { get; set; }
    public string MimeType { get; set; }
    public NameValueCollection Headers { get; private set; }

    public Stream GetResponse(IResponse response, out long responseLength, out string redirectUrl)
    {
        redirectUrl = null;
        responseLength = -1;

        response.MimeType = MimeType;
        response.StatusCode = StatusCode;
        response.StatusText = StatusText;
        response.ResponseHeaders = Headers;

        var memoryStream = Stream as MemoryStream;
        if (memoryStream != null)
        {
            responseLength = memoryStream.Length;
        }

        return Stream;
    }

    public bool ProcessRequestAsync(IRequest request, ICallback callback)
    {
        // The 'host' portion is entirely ignored by this scheme handler.
        var uri = new Uri(request.Url);
        var fileName = uri.AbsolutePath;
        string resource;

        if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
        {
            var resourceHandler = ResourceHandler.FromString(resource);
            stream = (MemoryStream)resourceHandler.Stream;

            var fileExtension = Path.GetExtension(fileName);
            mimeType = ResourceHandler.GetMimeType(fileExtension);

            callback.Continue();
            return true;
        }
        else
        {
            callback.Dispose();
        }

        return false;
    }

    void GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl)
    {
        responseLength = stream == null ? 0 : stream.Length;
        redirectUrl = null;

        response.StatusCode = (int)HttpStatusCode.OK;
        response.StatusText = "OK";
        response.MimeType = mimeType;
    }

    bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback)
    {
        //Dispose the callback as it's an unmanaged resource, we don't need it in this case
        callback.Dispose();

        if (stream == null)
        {
            bytesRead = 0;
            return false;
        }

        //Data out represents an underlying buffer (typically 32kb in size).
        var buffer = new byte[dataOut.Length];
        bytesRead = stream.Read(buffer, 0, buffer.Length);

        dataOut.Write(buffer, 0, buffer.Length);

        return bytesRead > 0;
    }

    bool CanGetCookie(Cookie cookie)
    {
        return true;
    }

    bool CanSetCookie(Cookie cookie)
    {
        return true;
    }

    void Cancel()
    {

    }

}

在这个类里,我定义了一个自定义资源字典,它将决定使用资源中的哪个文件。所以就像我在第一个例子中所说的那样,custom://cefsharp/assets/css/style.css应该加载资源Properties.Resources.style,但问题是一旦我进入到特定的URL,就没有任何东西被加载。我尝试输出mimeType,它可以工作,但是文件本身无法正确输出。我的实现有什么问题吗?
此外,我已经尝试以如下形式输出原始文件:
if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
{
    MessageBox.Show(resource);
}

它可以正确输出文件,没有任何问题。
在初始化 CefSharp 之前,我使用以下代码加载自定义 Scheme:
var settings = new CefSettings();
settings.RegisterScheme(new CefCustomScheme
{
    SchemeName = MyCustomSchemeHandlerFactory.SchemeName,
    SchemeHandlerFactory = new MyCustomSchemeHandlerFactory()
});

上述类基于以下链接: MyCustomSchemeHandlerFactory:FlashResourceHandlerFactory.cs MyCustomSchemeHandler:CefSharpSchemeHandler.csResourceHandler.cs

请使用与您的版本相匹配的分支示例,如果您正在使用 47.0.3,则使用 cefsharp/47 分支,而来自 master 的示例在 49.0.0 发布之前将无法正常工作。https://github.com/cefsharp/CefSharp/tree/cefsharp/47 - amaitland
2个回答

5

由于Cefsharp在过去几个月中有些变化,因此这里提供了一种更新且更容易处理“file”协议的方法。我在这个问题上写了一篇博客文章

您需要添加的是您的方案处理程序及其工厂:

using System;
using System.IO;
using CefSharp;

namespace MyProject.CustomProtocol
{
    public class CustomProtocolSchemeHandler : ResourceHandler
    {
        // Specifies where you bundled app resides.
        // Basically path to your index.html
        private string frontendFolderPath;

        public CustomProtocolSchemeHandler()
        {
            frontendFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "./bundle/");
        }

        // Process request and craft response.
        public override bool ProcessRequestAsync(IRequest request, ICallback callback)
        {
            var uri = new Uri(request.Url);
            var fileName = uri.AbsolutePath;

            var requestedFilePath = frontendFolderPath + fileName;

            if (File.Exists(requestedFilePath))
            {
                byte[] bytes = File.ReadAllBytes(requestedFilePath);
                Stream = new MemoryStream(bytes);

                var fileExtension = Path.GetExtension(fileName);
                MimeType = GetMimeType(fileExtension);

                callback.Continue();
                return true;
            }

            callback.Dispose();
            return false;
        }
    }

    public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
    {
        public const string SchemeName = "customFileProtocol";

        public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
        {
            return new CustomProtocolSchemeHandler();
        }
    }
}

在调用Cef.Initialize之前,需要先将其注册:

var settings = new CefSettings
{
  BrowserSubprocessPath = GetCefExecutablePath()
};

settings.RegisterScheme(new CefCustomScheme
{
  SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
  SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});

我遇到了这个错误:"'CustomProtocolSchemeHandler.ProcessRequestAsync(IRequest, ICallback)': 返回类型必须是'CefReturnValue',以匹配被覆盖的成员'ResourceHandler.ProcessRequestAsync(IRequest, ICallback)'" - SilenceKit

4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接