.NET(C#) CefSharp 下载获取页面中指定的文件图片视频等内容(.jpg、.js等)
1、下载获取一般文件内容实现思路
1) 首先需要对ChromiumWebBrowser
的 IRequestHandler
RequestHandler
进行实现。
2) 需要对IRequestHandler
的IResponseFilter
IRequestHandler.GetResourceResponseFilter
方法进行重写。
3) 需要写一个类实现 IResponseFilter
接口。
4) 然后就可用在IResponseFilter
的FilterStatus Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten)
方法中,读取指定内容的Stream。
2、下载文件示例代码
由于很多文件无法获取到完整内容,再者具体文件内容在Filter里面进行了控制,而Fileter的内容依赖于IRequestHandler
所以,外部只能操作Handler得到数据。 所以需要在,Filter和Hanlder类中,使用事件来传递具体的内容。代码如下。 Filter类如下:
public >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 >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 >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 >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#)cefsharpChrome 浏览器控件后台执行Iframe中的Js代码的方法
.NET(C#)cefsharp设置浏览器默认语言和userAgent及示例代码
.NET(C#) CefSharp CommandLine开关参数配置和读取网页源代码方法及示例代码