FrankerZ FrankerZ - 3 months ago 43
HTML Question

Fetching images with JavascriptResponse in CefBrowser (Without downloading twice)

I'm attempting to use

(Offscreen) to get image information in a webpage. I'd like to avoid downloading the content twice (I'm aware I can pull the src string from an image tag, and then download the image again). Right now, this is my code:

using (var browser = new ChromiumWebBrowser("http://example.com"))
{
//All this does is wait for the entire frame to be loaded.
await LoadPageAsync(browser);

var res1 = await browser.EvaluateScriptAsync("document.getElementsByTagName('img')[0].src");
//res1.Result = the source of the image (A string)

var res2 = await browser.EvaluateScriptAsync("document.getElementsByTagName('img')[0]");
//This causes an APPCRASH on CefSharp.BrowserSubprocess.exe
}


The way I figure it,
CefSharp
is already downloading these images to render a webpage. I'd like to avoid making a second request to pull these images from the client, and pull them directly from the client. Is this possible? What are the limitations of the
JavascriptResponse
object, and why is it causing an APPCRASH here?

Some thoughts: I thought about base64 encoding the image and then pulling it out this way, but this would require me to generate a canvas and fill that canvas every time for each image I want, generate a base64 string, bring it to c# as a string, and then decode it back to an image. I don't know how efficient that would be, but I'm hoping there could be a better solution.

Answer

This is how I solved it:

result = await browser.EvaluateScriptAsync(@"
;(function() {
    var getDataFromImg = function(img) {
        var canvas = document.createElement('canvas');
        var context = canvas.getContext('2d');
        context.drawImage(img, 0, 0 );
        var dataURL = canvas.toDataURL('image/png');

        return dataURL.replace(/^data:image\/(png|jpg);base64,/, '');
    }

    var images = document.querySelectorAll('.image');
    var finalArray = {};
    for ( var i=0; i<images.length; i++ )
    {
        //I just filled in array. Depending on what you're grabbing, you may want to fill
        //This with objects instead with text to identify each image.
        finalArray.push(getDataFromDiv(images[i]));
    }
    return finalArray;
})()");

    //Helper function for below
    private static string FixBase64ForImage(string image)
    {
        var sbText = new StringBuilder(image, image.Length);
        sbText.Replace("\r\n", string.Empty);
        sbText.Replace(" ", string.Empty);
        return sbText.ToString();
    }

    //In c# convert the data to a memory stream, and then load it from that.
    var bitmapData = Convert.FromBase64String(FixBase64ForImage(image));
    var streamBitmap = new MemoryStream(bitmapData);
    var sourceImage = (Bitmap) Image.FromStream(streamBitmap);