Travailler avec une page Web construite localement dans CefSharp

j'ai un navigateur CefSharp créé dans mon Winform et j'ai besoin de construire dynamiquement une page HTML en mémoire et ensuite avoir un rendu CefSharp.

idéalement, je voudrais passer au constructeur une chaîne avec le HTML mais il attend une URL. La réponse est probablement non, mais y a-t-il une directive avec laquelle vous pouvez préparer la chaîne pour faire savoir au CefSharp que c'est une chaîne qui contient une page web? Alors CefSharp va créer un fichier temp?

Si non, où est le Chrome le dossier temporaire est réglé? Il travaillera si j'écris un fichier de là et de passer ensuite que comme un chemin d'accès complet? Je sais que Chrome supporte quelque chose comme file:///Users/dmacdonald/Documents/myFile.htm en tant QU'URL, mais ne sait pas comment former une URL si vous utilisez la structure temp.

Voici mon nouveau code mais l'objet de mon navigateur n'a pas de propriété ResourceHandler. Je vois qu'il a un 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);
        }
    }
}
10
demandé sur user461051 2015-02-24 16:53:30

4 réponses

L'Approche Simple (un "fichier", une page)

LoadString () peut être utilisé pour charger directement à partir d'une chaîne de caractères:

ChromiumWebBrowser.LoadString(string html, string url);

alternativement,LoadHtml () peut charger à partir d'une chaîne dans un encodage donné:

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

j'ai essayé les deux, et ils semblent tous les deux fonctionner, au moins avec CefSharp.Wpf v51.0.0. Selon WebBrowserExtensions.cs,LoadHtml()RegisterHandler() pour vous inscrire ResourceHandler. Il est pas clair pour moi comment LoadString() fonctionne, mais les deux fonctions semblent avoir le même effet.

assurez-vous d'utiliser un format D'URL valide pour la fausse URL, comme:

https://myfakeurl.com

L'Approche Complexe (plusieurs "fichiers", tels que doc + images)

  1. Créer une classe dérivant de IResourceHandlerFactory. En utilisant VS2015, mousser sur le nom souligné en rouge devrait donner l'option de implémenter l'interface. Cette option complète automatiquement simplifie grandement création de la classe, afin d'être sûr de l'utiliser.

  2. Similaire à l'étape 1, créer une classe dérivant de IResourceHandler. Assurez-vous d'utiliser le implémenter l'interface auto-option complète si vous le pouvez.

  3. Dans la classe créée à l'étape 1 (dérivée de IResourceHandlerFactory), il y a une fonction appelée GetResourceHandler(). Dans cette fonction, retournez une nouvelle instance de votre classe dérivée de etape 2 (basé sur IResourceHandler). En utilisant new ceci est essentiel puisque le navigateur Web peut demander plusieurs fichiers simultanément. IResourceHandler l'instance doit traiter une requête du navigateur (pas de soucis, c'est fait pour vous).

  4. comme mentionné par OP, le contrôle du navigateur a un membre appelé ResourceHandlerFactory. Définir ce membre est égal à un instance de votre classe que vous avez créé dans etape 1 (dérivant de l' IResourceHandlerFactory). C'est ce qui relie le contrôle du navigateur Web de Chrome à votre classes d'interface. À l'étape 3, vous avez relié vos deux classes, donc nous avons une chaîne complète.

  5. dans la classe de l'étape 2, il y a une fonction appelée ProcessRequest(). C'est la première fonction appelée lorsqu'une demande est faite par une page Web. Votre but ici est d'enregistrer L'URL demandée et toutes les données POST, puis de décider si vous autorisez la requête, en appelant soit callback.Continue() ou callback.Cancel(). Retour true pour continuer.

  6. Nouveau dans la classe de l'étape 2, Il y a une fonction appelée GetResponseHeaders(). C'est la deuxième fonction appelée. Votre but ici est de vérifier L'URL, peut-être récupérer les données du fichier où vous les stockez (mais pas encore de les envoyer), déterminer la longueur de la réponse (Taille du fichier ou de la chaîne de caractères), et définir un code d'état approprié dans l'objet de réponse. Assurez-vous de définir toutes ces variables pour que la requête puisse être traitée correctement.

  7. votre dernière étape, encore une fois dans la classe de l'étape 2, est de remplir la demande au sein de la troisième fonction appelée: ReadResponse(). Dans cette fonction, écrivez vos données récupérées à l'étape 6 à dataOut stream. Si vos données dépassent environ 32kB, vous pouvez avoir besoin de l'envoyer en plusieurs morceaux. soyez absolument sûr de limiter le montant que vous écrivez dans un appel à la longueur de l' dataOut stream. Set bytesRead à tout ce que vous avez écrit dans cet appel particulier. Lors du dernier appel, lorsqu'il ne reste plus de données, il suffit de définir bytesRead à zéro et retour false. Parce que vous pouvez être appelé à plusieurs reprises pour un dossier donné, assurez-vous de suivre votre emplacement de lecture actuelle de sorte que vous savez où vous êtes et combien de données ont été envoyées.

pour ceux qui ne sont pas familiers avec la matière, vous pouvez stocker des fichiers de données directement compilés dans votre EXE en les ajoutant à votre projet et en définissant leur "action de construction" en "ressource embarquée", suivi de charger leurs données programmatiquement en utilisant System.Reflection.Assembly.GetManifestResourceStream(). En utilisant les méthodes ci-dessus, il n'y a pas de besoin de créer ou lire des fichiers à partir du disque.

10
répondu Michael 2016-09-20 18:39:30

voir https://github.com/cefsharp/CefSharp/blob/v39.0.0-pre02/CefSharp.Example/CefExample.cs#L44 pour un exemple d'enregistrement d'un ResourceHandler pour une chaîne en mémoire.

comme vous pouvez le voir, il a toujours une URL (les ressources web ont généralement tendance à l'avoir) mais il peut être un mannequin de votre choix.

Voici la recherche de GitHub pour la façon dont il est appelé dans les exemples d'applications WinForms (et WPF) : https://github.com/cefsharp/CefSharp/search?utf8=%E2%9C%93&q=RegisterTestResources

une autre option, probablement moins favorable, avec un fichier temp (anywhere?) dans le système de fichiers local est d'utiliser FileAccessFromFileUrlsAllowed

mise à Jourdans les commentaires ci-dessous:

quelle version du CefSharp utilisez-vous maintenant? Si vous regardez github.com/cefsharp/CefSharp/releases and search for resource vous voyez que L'API a changé version 49 ( regardez sous "breaking changes" pour cette version) - voir les commentaires ci-dessous pour plus de gotcha

2
répondu jornh 2016-09-09 17:38:16

voici un exemple d'usine personnalisée qui charge des ressources à partir du système de fichiers:

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);
    }
}

Et voici comment vous le feriez appliquer:

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");
1
répondu Luis Perez 2017-12-14 03:19:24

vous avez probablement besoin d'utiliser custom scheme handler, afin de servir les fichiers locaux, et de "contourner" la sécurité chromée concernant le protocole de fichiers.

j'ai écrit blog sur cette question.

ce que vous voulez ajouter est votre gestionnaire scheme et son usine:

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();
        }
    }
}

Et puis de l'enregistrer avant d'appeler Cef.Initialisation:

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

settings.RegisterScheme(new CefCustomScheme
{
  SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
  SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});
1
répondu bartosz.baczek 2018-04-24 20:14:56