1.PuppeteerSharp在asp.net中使用的坑:
1.如果使用nuget下载PuppeteerSharp 则还需要添加PuppeteerSharp.AspNetFramework包依赖。
2.需要下载chromium 浏览器文件 参考https://www.cnblogs.com/cdyy/p/PuppeteerSharp.html 这篇博客。
3.手动添加依赖包,参考如下:
a.通过nuget 添加以下包
左图是包版本,右图是需要下载的包。
2.将PuppeteerSharp 生命周期交给autofac管理
前提:一个asp.net 应用程序只需要启动一个chromium 进程,当寄宿在IIS中的asp.net应用程序 停止或被回收 时,chromium 进程也需要被关闭掉。
通过启动的chromium浏览器开启“Page”页,每一次的请求打开的page页(一个或多个),将在请求结束后自动销毁掉。
这里提供下思路,参考代码如下:
1).创建 IChromiumBrowser,这里封装了下PuppeteerSharp 的 Browser 对象。
public interface IChromiumBrowser : IDisposable,IAsyncDisposable { Task<Browser> GetBrowserAsync(); }
实现
public class ChromiumBrowserAspNet : IChromiumBrowser { private readonly LaunchOptions _launchOptions; public ChromiumBrowserAspNet() { _launchOptions = new LaunchOptions(); DetectBrowserFile(); } private Browser _browser; private bool _existsBrowserFile; private static readonly object Lock = new object(); public async Task<Browser> GetBrowserAsync() { if (_browser != null) return _browser; await Task.Run(() => { lock (Lock) { if (_browser == null) { //请提前下载好浏览器相关文件 //await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision); _browser = Puppeteer.LaunchAsync(_launchOptions).Result; //如果浏览器进程被其他操作强制关闭,释放_browser。 _browser.Disconnected += (a, b) => { if (_browser == null) return; Dispose(true); }; } } }); return _browser; } private void DetectBrowserFile() { if (_existsBrowserFile) return; string platForm = null; if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { platForm = Platform.MacOS.ToString(); } if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { platForm = Platform.Linux.ToString(); } if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { platForm = RuntimeInformation.OSArchitecture == Architecture.X64 ? Platform.Win64.ToString() : Platform.Win32.ToString(); } string folder = Path.Combine(Directory.GetCurrentDirectory(), ".local-chromium"); string filePath = Path.Combine(folder, $"{platForm}-{BrowserFetcher.DefaultRevision}"); _existsBrowserFile = new DirectoryInfo(filePath).Exists; if (!_existsBrowserFile) { throw new Exception($"{filePath}"); } } protected virtual void Dispose(bool disposing) { if (disposing) { _browser?.Dispose(); } _browser = null; } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } public async ValueTask DisposeAsync() { await DisposeAsyncCore(); Dispose(disposing: false); GC.SuppressFinalize(this); } protected virtual async ValueTask DisposeAsyncCore() { if (_browser != null) { await _browser.DisposeAsync(); } _browser = null; } }
2).通过browser创建page 页 。名字你自己取,我的叫IPuppeteerSharpContext
public interface IPuppeteerSharpContext : IDisposable, IAsyncDisposable { List<IPuppeteerSharpPage> PuppeteerSharpPages { get; } Task<List<IPuppeteerSharpPage>> GetPagesAsync(List<PageSetting> pageSettings);Task<IPuppeteerSharpPage> GetPageAsync(PageSetting pageSetting);
}
实现
public class PuppeteerSharpContextAspNet : IPuppeteerSharpContext { private readonly IChromiumBrowser _chromiumBrowser; public PuppeteerSharpContextAspNet(IChromiumBrowser chromiumBrowser) { _chromiumBrowser = chromiumBrowser; _puppeteerSharpPages = new List<IPuppeteerSharpPage>(); } private Browser _browser; private async Task StarUpPageAsync() { if (_browser == null) { _browser = await _chromiumBrowser.GetBrowserAsync(); } } private List<IPuppeteerSharpPage> _puppeteerSharpPages; public List<IPuppeteerSharpPage> PuppeteerSharpPages => _puppeteerSharpPages; public async Task<List<IPuppeteerSharpPage>> GetPagesAsync(List<PageSetting> pageSettings) { if (pageSettings == null) throw new Exception("pageSettings is null"); await StarUpPageAsync(); var list = new List<IPuppeteerSharpPage>(); pageSettings.ForEach(item => { var page = new PuppeteerSharpPage(item, _browser); list.Add(page); _puppeteerSharpPages.Add(page); }); return list; } public async Task<IPuppeteerSharpPage> GetPageAsync(PageSetting pageSetting) { if (pageSetting == null) throw new Exception("pageSetting is null"); await StarUpPageAsync(); var page = new PuppeteerSharpPage(pageSetting, _browser); _puppeteerSharpPages.Add(page); return page; } protected virtual void Dispose(bool disposing) { if (disposing) { _puppeteerSharpPages?.ForEach(item => item.Dispose()); } _puppeteerSharpPages = null; } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } public async ValueTask DisposeAsync() { await DisposeAsyncCore(); Dispose(disposing: false); GC.SuppressFinalize(this); } protected virtual async ValueTask DisposeAsyncCore() { if (_puppeteerSharpPages != null) { foreach (var item in _puppeteerSharpPages) { await item.DisposeAsync(); } } _puppeteerSharpPages = null; } }
IPuppeteerSharpPage和PuppeteerSharpPage 根据你自己情况去实现。
3).将ChromiumBrowser 、“Page”交给autofac管理,参考 https://www.cnblogs.com/bluesummer/p/9041802.html “IRegisteredObject”
/// <summary> /// Asp.Net IIS宿主环境配置 <see cref="WebAppEnviroment" />. /// </summary> public class WebAppEnviroment : IRegisteredObject { public WebAppEnviroment() { HostingEnvironment.RegisterObject(this); } private static IChromiumBrowser _chromiumBrowser; public void RegisterBrowserModule(ContainerBuilder builder) { builder.Register(context => { IChromiumBrowser browser = new ChromiumBrowserAspNet(); _chromiumBrowser = browser; return browser; }).As<IChromiumBrowser>().SingleInstance(); builder.RegisterType<PuppeteerSharpContextAspNet>().As<IPuppeteerSharpContext>().InstancePerRequest(); } public void Stop(bool immediate) { if (_chromiumBrowser != null) { _chromiumBrowser.Dispose(); _chromiumBrowser = null; } HostingEnvironment.UnregisterObject(this); } } public static class ContainerBuilderExtsionTes { public static void RegisterBrowserModule(this ContainerBuilder builder) { var app = new WebAppEnviroment(); app.RegisterBrowserModule(builder); } }
这里是将ChromiumBrowser创建成了单例,因为只需要存在一个(只需要存在一个browser 浏览器),IPuppeteerSharpContext 则是每次请求一个只创建一个实例,然后通过 IPuppeteerSharpContext去创建page页和释放page页。
大致思路就是这样,也不知道有没有讲清楚。 这里不提供具体实现代码。