CefSharp访问和操纵页面上的内容,可以以编程方式执行 JavaScript 并将其嵌入到页面中,并在触发JavaScript事件时接收回调。您可以使用CefSharp显示使用HTML5构建的嵌入式UI,或显示远程Web内容和Web应用程序。Google Chrome浏览器可以使用很多命令行(CommandLine)配置,有些更改功能的行为,而另一些则用于调试或试验。本文主要介绍.NET(C#)中, 使用CefSharp时,下载获取网页中地址的文件图片视频等内容(.jpg、.js等)的方法,以及实现的示例代码。

1、下载获取一般文件内容实现思路

1) 首先需要对ChromiumWebBrowser IRequestHandlerRequestHandler进行实现。
2) 需要对IRequestHandlerIResponseFilterIRequestHandler.GetResourceResponseFilter 方法进行重写。
3) 需要写一个类实现 IResponseFilter 接口。
4) 然后就可用在IResponseFilterFilterStatus Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten) 方法中,读取指定内容的Stream。

2、下载文件示例代码

由于很多文件无法获取到完整内容,再者具体文件内容在Filter里面进行了控制,而Fileter的内容依赖于IRequestHandler所以,外部只能操作Handler得到数据。 所以需要在,Filter和Hanlder类中,使用事件来传递具体的内容。代码如下。 Filter类如下:

public class TestImageFilter : IResponseFilter  
    {  
        public event Action<byte[]> NotifyData;  
        private int contentLength = 0;  
        private List<byte> dataAll = new List<byte>();  
        public void SetContentLength(int contentLength)  
        {  
            this.contentLength = contentLength;  
        }  
        public FilterStatus Filter(System.IO.Stream dataIn, out long dataInRead, System.IO.Stream dataOut, out long dataOutWritten)  
        {  
            try  
            {  
                if (dataIn == null)  
                {  
                    dataInRead = 0;  
                    dataOutWritten = 0;  
                    return FilterStatus.Done;  
                }  
                dataInRead = dataIn.Length;  
                dataOutWritten = Math.Min(dataInRead, dataOut.Length);  
                dataIn.CopyTo(dataOut);  
                dataIn.Seek(0, SeekOrigin.Begin);  
                byte[] bs = new byte[dataIn.Length];  
                dataIn.Read(bs, 0, bs.Length);  
                dataAll.AddRange(bs);  
                if (dataAll.Count == this.contentLength)  
                {  
                    // 通过这里进行通知  
                    NotifyData(dataAll.ToArray());  
                    return FilterStatus.Done;  
                }  
                else if (dataAll.Count < this.contentLength)  
                {  
                    dataInRead = dataIn.Length;  
                    dataOutWritten = dataIn.Length;  
                    return FilterStatus.NeedMoreData;  
                }  
                else  
                {  
                    return FilterStatus.Error;  
                }  
            }  
            catch (Exception ex)  
            {  
                dataInRead = dataIn.Length;  
                dataOutWritten = dataIn.Length;  
                return FilterStatus.Done;  
            }  
        }  
        public bool InitFilter()  
        {  
            return true;  
        }  
    }  
bool IRequestHandler.OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)  
        {  
            //NOTE: You cannot modify the response, only the request  
            // You can now access the headers  
            //var headers = response.ResponseHeaders;  
            try  
            {  
                var content_length = int.Parse(response.ResponseHeaders["Content-Length"]);  
                if (this.filter != null)  
                {  
                    this.filter.SetContentLength(content_length);  
                }  
            }  
            catch { }  
            return false;  
        }  
        private TestImageFilter filter = null;  
        public event Action<byte[]> NotifyData;  
        IResponseFilter IRequestHandler.GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)  
        {  
            var url = new Uri(request.Url);  
            if (url.AbsoluteUri.Contains("http://test.test.com/somehead?"))  
            {  
                this.filter = new TestImageFilter();  
                filter.NotifyData += filter_NotifyData;  
                return filter;  
            }  
            return null;  
        }  
        void filter_NotifyData(byte[] data)  
        {  
            if (NotifyData != null)  
            {  
                NotifyData(data);  
            }  
        } 

此方法位IRequestHandler的一部分实现,通过实现函数:IRequestHandler.GetResourceResponseFilter得到资源文件的长度,然后长度传入Filter,在Filter中控制从而得到整个数据的真正长度。

3、下载分片分段数据文件数据

部分站点,返回数据是分片了的,不能通过Content-Length的长度来判断,程序的Stream是否完成。

所以需要其他方式处理,单个http请求完成的时候,会调用Complete方法,所以可以在此方法中处理。示例代码如下,

public class FilterManager
    {
        private static Dictionary<string, IResponseFilter> dataList = new Dictionary<string, IResponseFilter>();
        public static IResponseFilter CreateFilter(string guid)
        {
            lock (dataList)
            {
                var filter = new TestImageFilter();
                dataList.Add(guid, filter);
                return filter;
            }
        }
        public static IResponseFilter GetFileter(string guid)
        {
            lock (dataList)
            {
                return dataList[guid];
            }
        }
    }
//对Stream进行合并
public class TestImageFilter : IResponseFilter
    {
        public List<byte> dataAll = new List<byte>();
        public FilterStatus Filter(System.IO.Stream dataIn, out long dataInRead, System.IO.Stream dataOut, out long dataOutWritten)
        {
            try
            {
                if (dataIn == null || dataIn.Length == 0)
                {
                    dataInRead = 0;
                    dataOutWritten = 0;
                    return FilterStatus.Done;
                }
                dataInRead = dataIn.Length;
                dataOutWritten = Math.Min(dataInRead, dataOut.Length);
                dataIn.CopyTo(dataOut);
                dataIn.Seek(0, SeekOrigin.Begin);
                byte[] bs = new byte[dataIn.Length];
                dataIn.Read(bs, 0, bs.Length);
                dataAll.AddRange(bs);
                dataInRead = dataIn.Length;
                dataOutWritten = dataIn.Length;
                return FilterStatus.NeedMoreData;
            }
            catch (Exception ex)
            {
                dataInRead = dataIn.Length;
                dataOutWritten = dataIn.Length;
                return FilterStatus.Done;
            }
        }
        public bool InitFilter()
        {
            return true;
        }
    }
//IRequestHandler实现代码
public class RequestHandler : IRequestHandler
    {
        // 略去代码 ...
        public event Action<byte[]> NotifyMsg;
        IResponseFilter IRequestHandler.GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
        {
            var url = new Uri(request.Url);
            if (url.AbsoluteUri.Contains("https://res.wx.qq.com/zh_CN/htmledition/v2/css/base/base2e4e03.css"))
            {
                var filter = FilterManager.CreateFilter(request.Identifier.ToString());
                return filter;
            }
            return null;
        }
        void filter_NotifyData(byte[] data)
        {
            if (NotifyMsg != null)
            {
                NotifyMsg(data);
            }
        }
        void IRequestHandler.OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
        {
            if (request.Url.Contains("https://res.wx.qq.com/zh_CN/htmledition/v2/css/base/base2e4e03.css"))
            {
                var filter = FilterManager.GetFileter(request.Identifier.ToString()) as TestImageFilter;
                filter_NotifyData(filter.dataAll.ToArray());
            }
        }
    }

相关文档:

https://github.com/cefsharp/CefSharp/wiki/Quick-Start

.Net(C#) cefsharp Chrome 浏览器控件后台执行Iframe中的Js代码的方法

.NET(C#) cefsharp 设置浏览器默认语言和userAgent及示例代码

.NET(C#) CefSharp CommandLine开关参数配置和读取网页源代码方法及示例代码