using HTEXScreen.Service; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PuppeteerSharp; using PuppeteerSharp.Media; using System.Configuration; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Web; namespace HTEXScreen.Controllers { [ApiController] [Route("screen")] public class ScreenController : ControllerBase { private readonly AzureStorageFactory _azureStorage; private readonly HttpClient _httpClient; private readonly IConfiguration _configuration; // private readonly HttpContext _httpContext; public ScreenController(HttpClient httpClient, AzureStorageFactory azureStorage, IConfiguration configuration) { _httpClient = httpClient; _azureStorage = azureStorage; _configuration = configuration; // _httpContext=httpContext; } /// /// 上传到blob /// /// /// [HttpPost("from-miniapp-delete")] [Authorize(Roles = "AClassONE")] [RequestSizeLimit(102_400_000_00)] //最大10000m左右 public async Task FromMiniAPPDelete(JsonElement json) { List? urls = json.GetProperty("urls").Deserialize>(); string? cnt = json.GetProperty("cnt").GetString(); string? test = json.GetProperty("test").GetString(); if (urls!=null) { foreach (var url in urls) { string dev = "Default"; if (test.Equals("Test", StringComparison.OrdinalIgnoreCase)) { dev="Test"; } string uri = url.Split("?")[0]; var blobinfo = BlobUrlString(uri); if (blobinfo.ContainerName.Equals(cnt)) { var uld= HttpUtility.UrlDecode(blobinfo.blob); bool ds= await _azureStorage.GetBlobContainerClient(cnt, dev).DeleteBlobIfExistsAsync(uld); } } } return Ok(); } private static (string ContainerName, string blob) BlobUrlString(string sasUrl) { sasUrl = sasUrl.Substring(8); string[] sasUrls = sasUrl.Split("/"); string ContainerName; ContainerName = sasUrls[1].Clone().ToString(); string item = sasUrls[0] + "/" + sasUrls[1] + "/"; string blob = sasUrl.Replace(item, ""); return (ContainerName, blob); } /// /// 上传到blob /// /// /// [HttpPost("from-miniapp-v2")] [Authorize(Roles = "AClassONE")] [RequestSizeLimit(102_400_000_00)] //最大10000m左右 public async Task FromMiniAPPV2() { try { List urls = new List(); var request = HttpContext.Request; var files =request.Form.Files; var cnt = request.Form["cnt"].ToString(); var test = request.Form["test"].ToString(); var path = request.Form["path"].ToString(); foreach (var file in files) { string dev = "Default"; if (test.Equals("Test", StringComparison.OrdinalIgnoreCase)) { dev="Test"; } if (path.EndsWith("/")) { path=path.Substring(0, path.Length -1); } (string name, string url) = await _azureStorage.GetBlobContainerClient(cnt, dev).UploadFileByContainer(file.OpenReadStream(), path, $"{file.FileName}", true); var auth = _azureStorage.GetBlobContainerClient(cnt, dev).GetBlobSasUriRead(_configuration, cnt, name, dev); var ext= file.FileName.Split("."); urls.Add(new { url= $"{url}?{auth.sas}",name =file.FileName, size=file.Length, extension= ext[ext.Length-1] , type = file.ContentType.Split("/")[0] ,blob= name ,cnt=cnt}) ; } return Ok(new { code=200,urls, }); } catch (Exception vErr) { return Ok(new { code=500,msg=vErr.Message}); } } /// /// 上传到blob /// /// /// [HttpPost("from-miniapp")] [Authorize(Roles = "AClassONE")] [RequestSizeLimit(102_400_000_00)] //最大10000m左右 public async Task FromMiniAPP([FromForm] IFormFile [] files,[FromForm] string path , [FromForm] string cnt, [FromForm] string test) { List urls = new List(); foreach (var file in files) { string dev = "Default"; if (test.Equals("Test", StringComparison.OrdinalIgnoreCase)) { dev="Test"; } if (path.EndsWith("/")) { path=path.Substring(0, path.Length -1); } (string name, string url) = await _azureStorage.GetBlobContainerClient(cnt, dev).UploadFileByContainer(file.OpenReadStream(), path, $"{file.FileName}", true); var auth= _azureStorage.GetBlobContainerClient(cnt, dev).GetBlobSasUriRead(_configuration,cnt, name,dev); urls.Add($"{url}?{auth.sas}" ); } return Ok(new { urls }); } [HttpPost("testbase64")] public async Task TestBase64(JsonElement json ) { JsonSerializerOptions option = new System.Text.Json.JsonSerializerOptions { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; string name = "dataid"; var reg = $"(?<=\\b{name}=)[^&]*"; Regex regex = new Regex(reg); string decode= HttpUtility.UrlDecode("https://localhost:7298/swagger/index.html?dataid=%E5%BC%A0%E5%90%8C%E5%AD%A6&bs64=1234567&dataid=%E6%9D%8E%E5%90%8C%E5%AD%A6"); Match match= Regex.Match(decode, reg); string a = ""; while (match.Success) { a =a+$"{match.Value}"; match = match.NextMatch(); } string s = JsonSerializer.Serialize(json, option); string das= HttpUtility.UrlEncode(s,Encoding.UTF8); byte[] byts = Encoding.UTF8.GetBytes(s); var rdata = Convert.ToBase64String(byts); byte[] bs = Convert.FromBase64String(rdata); string data = Encoding.UTF8.GetString(bs); return Ok(new { a, rdata,das }); } [HttpGet("download")] public async Task Download([FromQuery] ScreenshotDto screenshot) { try { HttpResponseMessage response = await _httpClient.GetAsync(screenshot.url); if (!string.IsNullOrWhiteSpace(screenshot?.url)) { string? url = screenshot?.url; string[] path = url.Split("/"); string fileName = path[path.Length - 1]; fileName=HttpUtility.UrlDecode(fileName); Stream stream = response.Content.ReadAsStream(); if (!Directory.Exists("Download")){ Directory.CreateDirectory("Download"); } FileStream fs = new FileStream($"Download/{fileName}", FileMode.Create); byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); stream.Seek(0, SeekOrigin.Begin); BinaryWriter bw = new BinaryWriter(fs); bw.Write(bytes); bw.Close(); fs.Close(); return Ok(screenshot); } else { return Ok("链接为空"); } } catch (Exception ex) { return Ok($"{ex.Message}{ex.StackTrace}"); } } /// /// C#使用Puppeteer http://t.zoukankan.com/zhaotianff-p-13528507.html /// 文档https://learnku.com/docs/puppeteer/3.1.0/class-request/8559 /// https://www.w3cschool.cn/puppeteer/puppeteer-gp1737se.html /// /// /// [HttpGet("screenshot-png")] public async Task ScreenshotPng([FromQuery] ScreenshotDto screenshot) { // 进入容器的命令 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"; 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; var browser = await Puppeteer.LaunchAsync(new LaunchOptions { ExecutablePath = BrowserExecutablePath, Headless = true, Args = new string[] { "--no-sandbox", "--disable-setuid-sandbox" } }); var page = await browser.NewPageAsync(); bool fullPage = true; await page.SetViewportAsync(new ViewPortOptions { Width = screenshot.width, Height = screenshot.height });; fullPage = false; await page.GoToAsync(System.Web.HttpUtility.UrlDecode(screenshot.url)); Thread.Sleep(screenshot.delay); string base64 = await page.ScreenshotBase64Async(new ScreenshotOptions { FullPage = fullPage, BurstMode = true }); //关闭浏览器 await browser.CloseAsync(); await browser.DisposeAsync(); return Ok(new { url = base64 ,type= "data:image/png;base64," }); } catch (Exception ex) { return BadRequest($"{ex.Message}\n{ex.StackTrace}"); } } [HttpPost("screenshot-pdf")] public async Task ScreenshotPdf(ScreenshotDto screenshot) { //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 name ,string url )> urls = new List<(string name, string url)>(); if (screenshot.urls.Count <= screenshot.pagesize) { urls.AddRange(await PageToPdfStream(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(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 Ok(new { urls= urls.Select(z=>z.url) }); } catch (Exception ex) { StreamWriter file = new StreamWriter("erorr_log.txt", append: true); await file.WriteLineAsync($"{this.GetType().Name} {JsonSerializer.Serialize(screenshot)}-----{ex.Message}----{ex.StackTrace}"); file.Close(); return BadRequest($"{ex.Message}\n{ex.StackTrace}"); } finally { if (browser != null && !browser.IsClosed) { await browser.CloseAsync(); await browser.DisposeAsync(); } } } private async Task> PageToPdfStream(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).UploadFileByContainer(stream, root, $"{id}.pdf", true)); } else { uploads.Add(_azureStorage.GetBlobContainerClient(cnt, name).UploadFileByContainer(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(); } private async Task PageToPdf(List urls, Browser browser, ViewPortOptions viewPortOptions, PdfOptions pdfOptions) { 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(System.Web.HttpUtility.UrlDecode(urls[i]), WaitUntilNavigation.Networkidle2)); } var responses_tasks = await Task.WhenAll(responses); List tasks = new List(); page_tasks.ToList().ForEach(x => { string file = $"{Guid.NewGuid()}.pdf"; tasks.Add(x.PdfAsync(file, pdfOptions)); }); await Task.WhenAll(tasks); tasks.Clear(); page_tasks.ToList().ForEach(x => { tasks.Add(x.DisposeAsync().AsTask()); }); await Task.WhenAll(tasks); } } } /// /// 网页截图参数 ///