Search code examples
c#webview2

How to save image in Microsoft WebView2 page to local file?


I want to download image from the page displayed in WebView2 control to local file. There's no API to manipulate page media except getting the page source. Should I use execScriptAsnyc to run script to get the image? No one page was found talking about this in my searching. Hope there's is answer from Overflow visitors.


Solution

  • Here is an example of how i did it by subscribing to event NavigationCompleted handler of my WebView instance:

    async void CoreWebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
    {
        if (!e.IsSuccess)
            return;
        string html = await webView.ExecuteScriptAsync("document.documentElement.outerHTML");
        if (html.Contains("any keyword to detect the page you are interested in"))
        {
            string img_link = ...; // extraction of the url (in my case the image is not rendered on the page but inside a css.
            // So i let webview handles all complexity of html request (cookie etc, site protection etc) by navigating on the url
            webView.Source = new Uri(link_from_css); // it triggers NavigationCompleted again! Code will continue on "else" part below
        }
        // assuming the only links that interests me are ending ".jpg"
        else if (webView.Source.AbsolutePath.EndsWith(".jpg"))
        {
            // No need to specify id of img element here, browser uses only one "img" html element in the page it generates to display the image
            // In you case you could call this directly from the code in the if/then part
            var imageData = await GetImageBytesAsync(null); 
            File.WriteAllBytes(@"C:\wherever_you_want\test.jpg", imageData);
        }
    }
    

    And the helper method:

    /// <summary>
    /// Get raw data (bytes) about an image in an "img" html element 
    /// where id is indicated by "elementId". 
    /// If "elementId" is null, the first "img" element in the page is used 
    /// </summary>
    async Task<byte[]> GetImageBytesAsync(string elementId = null, bool debug = false)
    {
        var script = @"
    function getImageAsBase64(imgElementId)
    {
        " + (debug ? "debugger;" : "") + @"
        let img = document.getElementById(imgElementId);
        if (imgElementId == '')
        {
            var results = document.evaluate('//img', document, null, XPathResult.ANY_TYPE, null);
            img = results.iterateNext();
        }
        let canvas = document.createElement('canvas');
        canvas.width = img.naturalWidth;
        canvas.height = img.naturalHeight;
    
        let ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight);
    
        let base64String = canvas.toDataURL('image/jpeg');  // or 'image/png'
        return base64String;
    };
    getImageAsBase64('" + elementId + "')";
        string base64Data = await webView.ExecuteScriptAsync(script);
        base64Data = base64Data.Split("base64,")[1].TrimEnd('"');
        var result = Convert.FromBase64String(base64Data);
        return result;
    }
    

    And voila!