C# Winform 使用 PuppeteerSharp 进行网页截图、生成pdf等操作

发布时间:2024-10-28 09:01

项目背景

        公司某项目上有一需求:要对合同、验收、付款申请单等审批页面进行截图,并生成Pdf文件回传到影像系统做最终的归档。然后衍生出来了一个WinForm的自动截图工具,其大致的实现逻辑是:定时请求数据库,抓取带截图的数据,组装得到审批页面地址,winfrom中引用WebView进行加载地址,在OnLoadingFinish加载完成方法里面,通过ScreenShot()获取等到Bitmap对象,然后在进行pdf文档的生成。

        弊端:因为是前后端分离的项目,OnLoadingFinish中无法判定ajax请求是否完毕,还需要定时器进行延迟截图、保存;其次是稳定性不高,可能是技术问题,经常导致程序奔溃,并且winform无法捕获到异常,但是另外一个项目崩溃几率很小,可能是数据量小吧

重构

        基于原项目使用的WebVIew,控件老旧,并且功能不太优化,所以采用了PuppeteerSharp进行了重构。

运行环境:Windows 服务器
开发环境:Vs@2017、.net framework 4.6以及以上,本项目4.8

PuppeteerSharp 

        Puppeteer是一个通过 DevTools Protocol 控制 headless chrome 的 high-level Node 库,也可以通过设置使用 非 headless Chrome。

我们手工可以在浏览器上做的事情,Puppeteer 都能胜任:

  1. 生成网页截图或者 PDF
  2. 爬取大量异步渲染内容的网页,基本就是人肉爬虫
  3. 模拟键盘输入、表单自动提交、UI 自动化测试        

        PuppeteerSharp是官方Node.JS Puppeteer API的.NET移植。

        1.由于Puppeteer-Sharp是NetStandard 2.0库,因此最低平台版本是.NET Framework 4.6.1和.NET Core 2.0

        2.支持WebSocket库的最低Windows版本是Windows 8和Windows Server 2012,具体参见 System.Net.WebSockets Namespace | Microsoft Docs

安装

        1.使用Nuget搜索PuppeteerSharp,找到PuppeteerSharp点击安装即可。

PuppeteerSharp的使用     

         1.获取Browsers对象

private async Task Browsers()
        {
            try
            {
                await slimLock.WaitAsync();
                // 已经拷贝了.local-chromium,不需要再下载了
                //await new BrowserFetcher().DownloadAsync();
                if (_browser == null)
                {
                    _browser = await Puppeteer.LaunchAsync(new LaunchOptions
                    {
                        Headless = true, 
                        DefaultViewport = null,
                        //Args = new string[] {
                        //    "--disable-infobars",//隐藏 自动化标题
                        //    "--no-sandbox", // 沙盒模式
                        //    "--start-maximized" // 最大化
                        //}
                    }); ;
                    return _browser;
                }
                return _browser;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                slimLock.Release();
            }
        }

        2.生成Pdf,注意看注释,方法中有俩种逻辑【一种是先生成图片,然后图片转成pdf;一种是官方的生成pdf的方法,只不过官方的会有分页,长页面看着不太优化,所以采用了第一种方式】

private async Task BrowsersPageToPdf(string loadUrl, string savePath)
        {
            var page = await _browser.NewPageAsync();
            // 设置页面分辨率
            await page.SetViewportAsync( new ViewPortOptions(){ Width = 1400, Height = 1050 });
            ScreenshotLoading("页面加载完成,等待数据渲染");
            //load: window.onload事件被触发时候完成导航,某些情况下它根本不会发生。
            //domcontentloaded: Domcontentloaded事件触发时候认为导航成功
            //networkidle0: 在 500ms 内没有网络连接时就算成功(全部的request结束),才认为导航结束
            //networkidle2: 500ms 内有不超过 2 个网络连接时就算成功(还有两个以下的request),就认为导航完成
            //加载时长 networkidle0 > networkidle2 > load > domcontentloaded
            await page.GoToAsync(loadUrl, new NavigationOptions() { WaitUntil = new WaitUntilNavigation[] { WaitUntilNavigation.Networkidle0 } });
            ScreenshotLoading("页面加载完成,数据渲染完成,页面截图中....");
            ScreenshotOptions screenshotOptions = new ScreenshotOptions();
            //screenshotOptions.Clip = new PuppeteerSharp.Media.Clip() { Height = 0, Width = 0, X = 0, Y = 0 };//设置截剪区域
            screenshotOptions.FullPage = true; //是否截取整个页面
            screenshotOptions.OmitBackground = false;//是否使用透明背景,而不是默认白色背景
            screenshotOptions.Quality = 100; //截图质量 0-100(png不可用)
            screenshotOptions.Type = ScreenshotType.Jpeg; //截图格式
            var saveFile = System.IO.Path.GetDirectoryName(savePath);
            if (!Directory.Exists(saveFile))
            {
                FileInfo fi = new FileInfo(saveFile);
                Directory.CreateDirectory(fi.DirectoryName);
            }
            string imgUrl = saveFile + "\\" +  CurrentHandlerData.DataKey + "_" + Guid.NewGuid() + ".jpg";
            await page.ScreenshotAsync(imgUrl, screenshotOptions);
            ScreenshotLoading("页面截图完成,正在转Pdf文件...");
            //var stream = await page.ScreenshotStreamAsync();
            CommonHelper.ConvertJpg2Pdf(imgUrl, savePath);
            ScreenshotLoading("页面截图完成,Pdf文件生成成功!");
            CommonHelper.FileDel(imgUrl);
            ScreenshotLoading("页面截图文件删除成功!");
            
            //官方生成Pdf方法
            //设置PDF选项
            //PdfOptions pdfOptions = new PdfOptions();
            //pdfOptions.DisplayHeaderFooter = false; //是否显示页眉页脚
            //pdfOptions.FooterTemplate = "";   //页脚文本
            //pdfOptions.Format = new PuppeteerSharp.Media.PaperFormat(11.27m, 30m);  //pdf纸张格式 英寸为单位 
            pdfOptions.Format = PaperFormat.A4;
            //pdfOptions.PrintBackground = true; // false pdf文件为灰白色,一些背景色也显示出来; true 页面为彩色
            //pdfOptions.HeaderTemplate = "";   //页眉文本
            //pdfOptions.Landscape = false;     //纸张方向 false-垂直 true-水平
            //pdfOptions.MarginOptions = new PuppeteerSharp.Media.MarginOptions() { Bottom = "0px", Left = "0px", Right = "0px", Top = "0px" }; //纸张边距,需要设置带单位的值,默认值是None
            //pdfOptions.Scale = 1m;            //PDF缩放,从0-1

            //await page.PdfAsync(savePath, pdfOptions);

            await page.DisposeAsync();
        }

坑点:

        1.puppeteer需要Chromium 内核用以加载网页,所以要先调用类库的下载sdk,他自动帮你下载Chromium ,这也可能导致第一次没有Chromium会卡一阵子,因为在下载 ,完成之后,再程序根目录【bin/Debug】下会发现.local-chromium 的一个文件夹

        2.获取Browser对象时,如果LaunchOptions.Headless设置为false,不能进行pdf文件生成,可以截图,同时可以看到程序调用的headless chrome的窗口

        3.LaunchOptions.DefaultViewport 为null,浏览的页面会默认浏览器窗口大小,也可以自定义其大小【没有试过】,否则的话,在chrome中可以看到,右边会有部分留白

        4.截图之前最好设置下分辨率:SetViewportAsync,否则图可能截不全

        5.有的复杂网页可能要加载一段时间,不然截图是空白或者不完整的,可以延时或者使用类库提供的等待方法【特别是前后端分离的项目,注意看生成pdf方法中的注释】

其他

        PuppeteerSharp还可以进行:向网页中注入HTML、执行Javascript(js)代码、连接到远程浏览器等等其他功能,也可以用于爬虫场景,其他功能可以看下官方介绍

        图片转Pdf方法:

public static void ConvertJpg2Pdf(string jpgfile, string pdf)
        {

            var document = new Document();
            using (var stream = new FileStream(pdf, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                var pdfWriter = PdfWriter.GetInstance(document, stream);
                using (var imageStream = new FileStream(jpgfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    var image = iTextSharp.text.Image.GetInstance(imageStream);
                    pdfWriter.CloseStream = false;
                    document.SetPageSize(new iTextSharp.text.Rectangle(image.Width + 72f, image.Height + 72f));//(WWidth + 72f, HHeight + 72f));
                    document.Open();
                    image.Alignment = iTextSharp.text.Image.ALIGN_MIDDLE;
                    document.Add(image);
                }
                document.Close();
            }
        }

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号