Работа с локально построенной веб-страницей в CefSharp

У меня есть браузер CefSharp, созданный в моей Winform, и мне нужно динамически создавать HTML-страницу в памяти, а затем отображать ее CefSharp.

В идеале я хотел бы передать конструктору строку с HTML в ней, но он ожидает URL. Ответ, вероятно, нет, но есть ли директива, с которой вы можете добавить строку, чтобы CefSharp знал, что это строка, содержащая веб-страницу? Тогда CefSharp создаст временный файл?

если нет, то где Chromium temp папка установлена в? Будет ли это работать, если я напишу файл туда, а затем передам его как полный путь? Я знаю, что Chrome будет поддерживать что-то вроде file:///Users/dmacdonald/Documents/myFile.htm как URL, но не уверен, как сформировать URL-адрес при использовании структуры temp.

вот мой новый код, но мой объект браузера не имеет свойства ResourceHandler. Я вижу, что у него есть ResourceHandlerFactory

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CefSharp.WinForms;
using CefSharp;


namespace DanCefWinForm
{
    public partial class Form1 : Form
    {
        public const string TestResourceUrl = "http://maps/resource/load";

        public Form1()
        {
            InitializeComponent();


        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ChromiumWebBrowser browser = new ChromiumWebBrowser("http://maps/resource/load")
            {
                Dock = DockStyle.Fill,
            };

            var handler = browser.ResourceHandler;

           browser.Location = new Point(20, 20);
           browser.Size = new Size(100, 100);
            this.Controls.Add(browser);
        }
    }
}

4 ответов


простой подход (один "файл", одна страница)

методом loadstring() может использоваться для загрузки непосредственно из строки:

ChromiumWebBrowser.LoadString(string html, string url);

кроме того, LoadHtml() загрузить из строки в кодировке:

ChromiumWebBrowser.LoadHtml(string html, string url, Encoding encoding);

я пробовал оба, и они оба, кажется, работают, по крайней мере с CefSharp.В WPF v51.0.0. Согласно WebBrowserExtensions.cs, LoadHtml() использует RegisterHandler() зарегистрировать ResourceHandler. Это не ясно мне, как LoadString() работает, но обе функции, похоже, имеют одинаковый эффект.

будьте уверены, чтобы использовать URL-адреса для поддельных URL-адреса, такие как:

https://myfakeurl.com

комплексный подход (несколько "файлов", таких как doc + изображения)

  1. создайте класс, производный от IResourceHandlerFactory. Используя VS2015, mousing над Красным подчеркнутым именем должен дать опцию реализует интерфейс. Этот auto-complete вариант значительно упрощает создание класса, поэтому обязательно используйте его.

  2. подобно шагу 1, Создайте класс, производный от IResourceHandler. Обязательно используйте реализует интерфейс опция автозаполнения, если вы можете.

  3. в классе, созданном на шаге 1 (производный от IResourceHandlerFactory), есть функция под названием GetResourceHandler(). В этой функции верните новый экземпляр производного класса из Шаг 2 (исходя из IResourceHandler). Используя new здесь очень важно, так как веб-браузер может запросить несколько файлов одновременно. Каждый IResourceHandler экземпляр должен обрабатывать один запрос из браузера (не беспокойтесь, это сделано для вас).

  4. как упоминалось OP, элемент управления браузером имеет член под названием ResourceHandlerFactory. Установите этот член равным новая экземпляр класса, который вы создали в Шаг 1 (извлечение из IResourceHandlerFactory). Это ссылки на веб-хрома управления браузера класс интерфейса. На Шаге 3 Вы связали оба своих класса, поэтому у нас есть полная цепочка.

  5. в классе с шага 2 есть функция под названием ProcessRequest(). Это первая функция, вызываемая при выполнении запроса веб-страницей. Ваша цель здесь-записать запрошенный URL и любые данные POST, а затем решить, разрешить ли запрос, вызвав либо callback.Continue() или callback.Cancel(). Возвращение правда для продолжения.

  6. снова в класс из шага 2, есть функция под названием GetResponseHeaders(). Это вторая вызванная функция. Ваша цель здесь-проверить URL-адрес, возможно, извлекая данные файла из того места, где вы его храните (но еще не отправляете), определить длину ответа (размер файла или строки) и установить соответствующий код состояния в объекте ответа. Обязательно установите все эти переменные, чтобы запрос мог выполняться правильно.

  7. ваш последний шаг, снова в классе с шага 2, должен завершиться запрос в пределах третьей вызываемой функции:ReadResponse(). В рамках этой функции запишите данные, полученные на шаге 6, в dataOut поток. Если ваши данные превышают 32 КБ, вам может потребоваться отправить их несколькими кусками. будьте абсолютно уверены, чтобы ограничить сумма, которую вы пишете в данном вызове на длину dataOut поток. Set bytesRead все, что вы написали в этом конкретном вызове. При последнем вызове, когда больше данных не остается, просто установите bytesRead до нуля и возврат false. Поскольку вы можете быть вызваны несколько раз для данного файла, не забудьте отслеживать текущее местоположение чтения, чтобы вы знали, где вы находитесь и сколько данных было отправлено.

для тех, кто не знаком с этим вопросом, Вы можете хранить файлы данных, непосредственно скомпилированные в EXE, добавив их в свой проект и установив их " действие сборки "на" встроенный ресурс", а затем загрузить свои данные программно с помощью System.Reflection.Assembly.GetManifestResourceStream(). Используя вышеуказанные методы,нет необходимо создать или прочитать любые файлы с диска.


см https://github.com/cefsharp/CefSharp/blob/v39.0.0-pre02/CefSharp.Example/CefExample.cs#L44 для примера регистрации ResourceHandler для строки в памяти.

как вы можете видеть, он по-прежнему имеет URL (веб-ресурсы, как правило, имеют это), но он может быть фиктивным по вашему выбору.

вот поиск GitHub для того, как он вызывается в примерах приложений WinForms (и WPF) : https://github.com/cefsharp/CefSharp/search?utf8=%E2%9C%93&q=RegisterTestResources

другой, вероятно, менее благоприятный вариант с временным файлом (где угодно?) в локальной файловой системе использовать FileAccessFromFileUrlsAllowed

обновление из комментариев ниже:

какую версию CefSharp вы сейчас используете? Примечание Если вы посмотрите на github.com/cefsharp/CefSharp/releases и ищите resource вы видите, что API изменился в версия 49 (посмотрите в разделе breaking changes для этой версии) - см. комментарии ниже для furtther gotcha's


вот пример пользовательской фабрики, которая загружает ресурсы из файловой системы:

public class FileResourceHandlerFactory : ISchemeHandlerFactory {
    private string scheme, host, folder, default_filename;

    public string Scheme => scheme;

    public FileResourceHandlerFactory(string scheme, string host, string folder, string default_filename = "index.html") {
        this.scheme = scheme;
        this.host = host;
        this.folder = folder;
        this.default_filename = default_filename;
    }

    private string get_content(Uri uri, out string extension) {
        var path = uri.LocalPath.Substring(1);
        path = string.IsNullOrWhiteSpace(path) ? this.default_filename : path;
        extension = Path.GetExtension(path);
        return File.ReadAllText(Path.Combine(this.folder, path));
    }

    IResourceHandler ISchemeHandlerFactory.Create(IBrowser browser, IFrame frame, string schemeName, IRequest request) {
        var uri = new Uri(request.Url);
        return ResourceHandler.FromString(get_content(uri, out var extension), extension);
    }
}

и вот как вы его примените:

var settings = new CefSettings();
settings.RegisterScheme(new CefCustomScheme {
    SchemeName = "app",
    SchemeHandlerFactory = fileResourceHandlerFactory,
    IsSecure = true //treated with the same security rules as those applied to "https" URLs
});
var chromeBrowser = new ChromiumWebBrowser();
chromeBrowser.Load("app://local");

вероятно, вам нужно использовать пользовательский обработчик схемы, чтобы обслуживать локальные файлы и "обходить" безопасность chromium в отношении файлового протокола.

я писал блоге по этому вопросу.

то, что вы хотите добавить, - это ваш обработчик схемы и его фабрика:

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.Инициализировать:

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

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