PuppeteerSharp 在asp.net中使用 PuppeteerSharp生命周期管理
2020-12-22 更新,以下代码有坑不要使用,回过头来看,发现以前写的东西好烂呀,哈哈哈哈。。。
博客就放这里给自己留个纪念。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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页。

大致思路就是这样,也不知道有没有讲清楚。 这里不提供具体实现代码。
本文作者:youliCC

浙公网安备 33010602011771号