using Microsoft.Azure.Cosmos; using PuppeteerSharp; using PuppeteerSharp.Media; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; using System.Web; using TEAMModelOS.SDK.DI; namespace HTEXScreen.Service { public class ScreenService { public static async Task UpdateStuArtPDF(IEnumerable urls, ScreenshotDto screenshot, AzureRedisFactory _azureRedisFactory, AzureCosmosFactory _azureCosmosFactory) { var env = screenshot.env.Equals("release") ? "Default" : "Test"; long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); List> responses = new List>(); List> redisSaves = new List>(); foreach (var url in urls) { //https://teammodeltest.blob.core.chinacloudapi.cn/hbcn/art/e9a5ec36-7299-45dc-9517-7457960346c4/report/202106005.pdf var uri = HttpUtility.UrlDecode(url); var paths = uri.Split("/art/"); if (paths.Length == 2) { var ps= paths[1].Split("/"); if (ps.Length == 3) { Uri uris = new Uri(paths[0]); // 获取URL的Segments属性,这是一个String数组,包含URL中的每个部分 string[] segments = uris.Segments; string key = $"ArtPDF:{ps[0]}"; // 确保segments数组至少有一个元素 if (segments.Length > 0) { // 获取数组中的最后一个元素,即最后一个'/'之后的部分 string lastSegment = segments[segments.Length - 1]; key = $"ArtPDF:{ps[0]}:{lastSegment}"; } string field = ps[2].Replace(".pdf", "",StringComparison.OrdinalIgnoreCase); var value = _azureRedisFactory.GetRedisClient(8, env).HashGet(key, field); if (value.HasValue && !value.IsNullOrEmpty) { JsonNode node = JsonNode.Parse(value.ToString()); if (node != null) { var pdfNode= node["pdf"]; var id = node["id"]; var code = node["code"]; if (pdfNode != null) { pdfNode["url"] = uri; pdfNode["blob"] = $"/art/{paths[1]}"; pdfNode["createTime"] = now; pdfNode["prime"] = true;//此处的作用是判断是否已经生成OK. node["pdf"] = pdfNode; } else { pdfNode = new JsonObject(); pdfNode["url"] = uri; pdfNode["blob"] = $"/art/{paths[1]}"; pdfNode["createTime"] = now; pdfNode["prime"] = true;//此处的作用是判断是否已经生成OK. node["pdf"] = pdfNode; } string json = node.ToJsonString(); redisSaves.Add(_azureRedisFactory.GetRedisClient(8, env).HashSetAsync(key, field, json)); byte[] bytes = Encoding.UTF8.GetBytes(json); var memoryStream = new MemoryStream(bytes); responses.Add(_azureCosmosFactory.GetCosmosClient(null,env).GetContainer("TEAMModelOS", "Student").ReplaceItemStreamAsync(memoryStream, $"{id}", new PartitionKey($"{code}"))); } } } } } await Task.WhenAll(redisSaves); await Task.WhenAll(responses); } public static async Task> ScreenshotPdf(ScreenshotDto screenshot, AzureStorageFactory _azureStorage) { //W3C School教程 https://www.w3cschool.cn/puppeteer/puppeteer-rip537tj.html // 进入容器的命令 docker exec -it f9e27d175498 /bin/bash //依赖包 https://blog.csdn.net/weixin_45447477/article/details/115188938 //sudo apt-get install libgdk-pixbuf2.0-0 libgdk-pixbuf-xlib-2.0-0 libdbusmenu-gtk3-4 libdbusmenu-glib4 libindicator3-7 ca-certificates fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils -y //解决ubuntu18上使用puppeteer https://blog.csdn.net/qq_42414062/article/details/114539378 //https://www.hardkoded.com/blog/running-puppeteer-sharp-azure-functions 使用。 //string url = "https://teammodelos.blob.core.chinacloudapi.cn/0-public/pie-borderRadius.html"; Browser browser = null; try { var bfOptions = new BrowserFetcherOptions(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { string dir = "/app"; if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } bfOptions.Path = dir; } var bf = new BrowserFetcher(bfOptions); var revisionInfo = bf.DownloadAsync(BrowserFetcher.DefaultChromiumRevision).Result; string BrowserExecutablePath = revisionInfo.ExecutablePath; browser = await Puppeteer.LaunchAsync(new LaunchOptions { ExecutablePath = BrowserExecutablePath, Headless = true, Args = new string[] { "--no-sandbox", "--disable-setuid-sandbox" } }); double unitPX = 37.7813; ViewPortOptions viewPortOptions = new ViewPortOptions { // Width = (int)Math.Ceiling(unitPX * 21), // Height = (int)Math.Ceiling(unitPX * 29.7 * 1) }; //ViewPortOptions viewPortOptions = new ViewPortOptions //{ // Width = screenshot.width, // Height = screenshot.height //}; PdfOptions pdfOptions = new PdfOptions { DisplayHeaderFooter = true, FooterTemplate = "", PreferCSSPageSize = true, Format = PaperFormat.A4 }; //ScreenshotOptions screenshotOptions= new ScreenshotOptions { FullPage = fullPage, BurstMode = true }; List<(string anme ,string url )> urls = new List<(string name, string url)>(); if (screenshot.urls.Count <= screenshot.pagesize) { urls.AddRange(await PageToPdfStream(_azureStorage,screenshot.urls, screenshot.fileNameKey, screenshot.cnt, screenshot.root, screenshot.env, browser, viewPortOptions, pdfOptions)); } else { List>> tasks = new List>>(); int pages = (screenshot.urls.Count + screenshot.pagesize) / screenshot.pagesize; for (int i = 0; i < pages; i++) { var lists = screenshot.urls.Skip((i) * screenshot.pagesize).Take(screenshot.pagesize).ToList(); tasks.Add(PageToPdfStream(_azureStorage,lists, screenshot.fileNameKey, screenshot.cnt, screenshot.root, screenshot.env, browser, viewPortOptions, pdfOptions)); } var tsk = await Task.WhenAll(tasks); foreach (var ts in tsk) { urls.AddRange(ts); } } //browser.NewPageAsync(); //foreach (var url in screenshot.urls) { // var page = await browser.NewPageAsync(); // await page.SetViewportAsync(viewPortOptions); // string file = $"E://pdfs//{Guid.NewGuid().ToString()}.pdf"; // var respons = await page.GoToAsync(System.Web.HttpUtility.UrlDecode(url), WaitUntilNavigation.Networkidle2); // if (respons.Ok) // { // await page.PdfAsync(file, pdfOptions); // // string base64 = await page.ScreenshotBase64Async(screenshotOptions); // } //} //关闭浏览器 await browser.CloseAsync(); await browser.DisposeAsync(); return urls; } catch (Exception ex) { StreamWriter file = new StreamWriter("erorr_log.txt", append: true); await file.WriteLineAsync($"ScreenService {JsonSerializer.Serialize(screenshot)}-----{ex.Message}----{ex.StackTrace}"); file.Close(); return new List<(string name, string url)>(); //return BadRequest($"{ex.Message}\n{ex.StackTrace}"); } finally { if (browser != null && !browser.IsClosed) { await browser.CloseAsync(); await browser.DisposeAsync(); } } } private static async Task> PageToPdfStream(AzureStorageFactory _azureStorage,List urls, string fileNameKey, string cnt, string root, string env, Browser browser, ViewPortOptions viewPortOptions, PdfOptions pdfOptions) { string name = env.Equals("release") ? "Default" : "Test"; List> pages = new List>(); urls.ForEach(x => { pages.Add(browser.NewPageAsync()); }); var page_tasks = await Task.WhenAll(pages); List> responses = new List>(); page_tasks.ToList().ForEach(x => { x.SetViewportAsync(viewPortOptions); }); for (int i = 0; i < urls.Count; i++) { responses.Add(page_tasks[i].GoToAsync(urls[i], 30000, new WaitUntilNavigation[] { WaitUntilNavigation.Networkidle2 })); } var responses_tasks = await Task.WhenAll(responses); //List> streams = new List>(); List tasks = new List(); List> uploads = new List>(); foreach (var page_task in page_tasks) { string url = page_task.Url; string[] paths = HttpUtility.UrlDecode(url).Split("/"); var reg = $"(?<=\\b{fileNameKey}=)[^&]*"; Regex regex = new Regex(reg); string decode = HttpUtility.UrlDecode(url); Match match = Regex.Match(decode, reg); string id = ""; while (match.Success) { id = id + $"{match.Value}"; match = match.NextMatch(); } //需要解析参数。paths[paths.Length-1] Stream stream = await page_task.PdfStreamAsync(pdfOptions); if (string.IsNullOrWhiteSpace(cnt)) { uploads.Add(_azureStorage.GetBlobContainerClient("teammodelos", name).UploadFileByContainerBName(stream, root, $"{id}.pdf", true)); } else { uploads.Add(_azureStorage.GetBlobContainerClient(cnt, name).UploadFileByContainerBName(stream, root, $"{id}.pdf", true)); } } (string name ,string url )[] uploadUrls = await Task.WhenAll(uploads); page_tasks.ToList().ForEach(x => { tasks.Add(x.DisposeAsync().AsTask()); }); await Task.WhenAll(tasks); return uploadUrls.ToList(); } } public class ScreenshotDto { public int width { get; set; } = 1920; public int height { get; set; } = 1080; public string? url { get; set; } /// /// 批量地址 /// public List urls { get; set; } = new List(); /// /// 提取参数的唯一id作为文件名 /// public string? fileNameKey { get; set; } /// /// 存在哪个容器里 /// public string? cnt { get; set; } public int delay { get; set; } public int pagesize { get; set; } = 5; public string? root { get; set; } public string? env { get; set; } = "release"; public string? msgId { get; set; } } }