发布时间: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
Puppeteer是一个通过 DevTools Protocol 控制 headless chrome 的 high-level Node 库,也可以通过设置使用 非 headless Chrome。
我们手工可以在浏览器上做的事情,Puppeteer 都能胜任:
- 生成网页截图或者 PDF
- 爬取大量异步渲染内容的网页,基本就是人肉爬虫
- 模拟键盘输入、表单自动提交、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点击安装即可。
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();
}
}