You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP MVC 5中调用Puppeteer Sharp二次生成PDF超时崩溃问题

问题根因

你的代码核心问题是每次调用ViewAsPdf方法都会重复执行Chromium下载校验、重新启动浏览器实例,频繁的进程创建销毁会快速耗尽系统内存、端口、句柄资源,即使每次调用关闭逻辑,也会因为Chromium进程退出延迟导致资源泄漏,最终出现后续请求加载超时、程序崩溃的问题。

核心优化点

  • 移除每次请求都执行的browserFetcher.DownloadAsync逻辑,Chromium下载只需要在应用启动时执行一次
  • 将Chromium浏览器实例改为单例复用,避免每次请求都创建销毁进程
  • 给页面跳转方法加上超时限制,避免异常场景下无限等待
  • 补充Chromium启动参数,适配服务器运行环境,减少不必要的资源占用

优化后代码

using PuppeteerSharp;
using System.Threading.Tasks;
using System.Web.Hosting;
using System;

namespace System.Web.Mvc
{
    public static class MvcPdfExtensions
    {
        // 单例复用浏览器实例
        private static readonly Lazy<Task<IBrowser>> _browserLazy = new Lazy<Task<IBrowser>>(InitBrowserAsync);

        private static async Task<IBrowser> InitBrowserAsync()
        {
            var hostPath = HostingEnvironment.MapPath("~/Content/");
            var browserFetcher = new BrowserFetcher(new BrowserFetcherOptions()
            {
                Path = hostPath
            });
            // 仅初始化时下载一次Chromium
            var revisionInfo = await browserFetcher.DownloadAsync(BrowserFetcher.DefaultChromiumRevision);
            return await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true, // 服务器环境建议开启无头模式减少资源占用
                ExecutablePath = revisionInfo.ExecutablePath,
                Args = new [] { "--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage" } // 适配服务器环境的启动参数
            });
        }

        public static async Task<ActionResult> ViewAsPdf(this Controller controller,
            string fileName,
            string actionName,
            string controllerName,
            object routeValues = null)
        {
            // 复用全局浏览器实例
            var browser = await _browserLazy.Value;
            var page = await browser.NewPageAsync();
            var cookies = controller.Request.Cookies.AllKeys;
            foreach (var cookieKey in cookies)
            {
                var cookie = controller.Request.Cookies[cookieKey];
                await page.SetCookieAsync(new CookieParam
                {
                    Name = cookie.Name,
                    Value = cookie.Value,
                    Domain = controller.Request.Url.Host, // 不要带端口,避免Cookie设置失效导致页面重定向
                    Path = "/"
                });
            }
            try
            {
                var url = controller.Url.Action(actionName, controllerName, routeValues, "http");
                var waitOptions = new WaitUntilNavigation[]
                {
                    WaitUntilNavigation.Load
                };
                // 增加15秒超时,避免无限等待
                await page.GoToAsync(url, 15000, waitOptions);
                var pdfOptions = new PdfOptions
                {
                    PrintBackground = true
                };
                var file = await page.PdfDataAsync(pdfOptions);
               
                return new FileContentResult(file, "application/pdf")
                {
                    FileDownloadName = fileName
                };
            }
            catch (Exception e)
            {
                return new ContentResult {
                    Content = $"生成PDF错误:类型:{e.Source} 错误信息:{e.Message}"
                };
            }
            finally {
                await page.DisposeAsync();
                // 浏览器实例不要关闭,留着复用
            }
        }

        // 应用停止时调用该方法释放浏览器资源,可在Global.asax的Application_End事件中调用
        public static async Task DisposeBrowserAsync()
        {
            if (_browserLazy.IsValueCreated)
            {
                await (await _browserLazy.Value).CloseAsync();
            }
        }
    }
}

额外注意事项

  • 如果是IIS部署环境,需要给应用程序池的标识开启允许加载用户配置文件选项,否则Chromium启动会失败
  • 静态单例浏览器实例会在应用池回收时自动释放,也可以主动在Application_End事件中调用MvcPdfExtensions.DisposeBrowserAsync()手动释放
  • 如果出现页面加载超时,先直接访问生成PDF的URL确认页面本身是否可以正常打开,排除业务代码本身的响应慢问题

内容的提问来源于stack exchange,提问作者David Ortega

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 10:51:02