ScreenController.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. using HTEXScreen.Service;
  2. using Microsoft.AspNetCore.Mvc;
  3. using PuppeteerSharp;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. using System.Text.Json;
  7. using System.Text.RegularExpressions;
  8. using System.Web;
  9. namespace HTEXScreen.Controllers
  10. {
  11. [ApiController]
  12. [Route("screen")]
  13. public class ScreenController : ControllerBase
  14. {
  15. private readonly AzureStorageFactory _azureStorage;
  16. private readonly HttpClient _httpClient;
  17. public ScreenController(HttpClient httpClient, AzureStorageFactory azureStorage) {
  18. _httpClient = httpClient;
  19. _azureStorage = azureStorage;
  20. }
  21. [HttpPost("testbase64")]
  22. public async Task<IActionResult> TestBase64(JsonElement json ) {
  23. JsonSerializerOptions option = new System.Text.Json.JsonSerializerOptions
  24. {
  25. Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
  26. };
  27. string name = "dataid";
  28. var reg = $"(?<=\\b{name}=)[^&]*";
  29. Regex regex = new Regex(reg);
  30. 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");
  31. Match match= Regex.Match(decode, reg);
  32. string a = "";
  33. while (match.Success)
  34. {
  35. a =a+$"{match.Value}";
  36. match = match.NextMatch();
  37. }
  38. string s = JsonSerializer.Serialize(json, option);
  39. string das= HttpUtility.UrlEncode(s,Encoding.UTF8);
  40. byte[] byts = Encoding.UTF8.GetBytes(s);
  41. var rdata = Convert.ToBase64String(byts);
  42. byte[] bs = Convert.FromBase64String(rdata);
  43. string data = Encoding.UTF8.GetString(bs);
  44. return Ok(new { a, rdata,das });
  45. }
  46. [HttpGet("download")]
  47. public async Task<IActionResult> Download([FromQuery] ScreenshotDto screenshot) {
  48. try {
  49. HttpResponseMessage response = await _httpClient.GetAsync(screenshot.url);
  50. if (!string.IsNullOrWhiteSpace(screenshot?.url))
  51. {
  52. string? url = screenshot?.url;
  53. string[] path = url.Split("/");
  54. string fileName = path[path.Length - 1];
  55. fileName=HttpUtility.UrlDecode(fileName);
  56. Stream stream = response.Content.ReadAsStream();
  57. if (!Directory.Exists("Download")){
  58. Directory.CreateDirectory("Download");
  59. }
  60. FileStream fs = new FileStream($"Download/{fileName}", FileMode.Create);
  61. byte[] bytes = new byte[stream.Length];
  62. stream.Read(bytes, 0, bytes.Length);
  63. stream.Seek(0, SeekOrigin.Begin);
  64. BinaryWriter bw = new BinaryWriter(fs);
  65. bw.Write(bytes);
  66. bw.Close();
  67. fs.Close();
  68. return Ok(screenshot);
  69. }
  70. else {
  71. return Ok("链接为空");
  72. }
  73. } catch (Exception ex) {
  74. return Ok($"{ex.Message}{ex.StackTrace}");
  75. }
  76. }
  77. /// <summary>
  78. /// C#使用Puppeteer http://t.zoukankan.com/zhaotianff-p-13528507.html
  79. /// 文档https://learnku.com/docs/puppeteer/3.1.0/class-request/8559
  80. /// https://www.w3cschool.cn/puppeteer/puppeteer-gp1737se.html
  81. /// </summary>
  82. /// <param name="screenshot"></param>
  83. /// <returns></returns>
  84. [HttpGet("screenshot-png")]
  85. public async Task<IActionResult> ScreenshotPng([FromQuery] ScreenshotDto screenshot)
  86. {
  87. // 进入容器的命令 docker exec -it f9e27d175498 /bin/bash
  88. //依赖包 https://blog.csdn.net/weixin_45447477/article/details/115188938
  89. //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
  90. //解决ubuntu18上使用puppeteer https://blog.csdn.net/qq_42414062/article/details/114539378
  91. //https://www.hardkoded.com/blog/running-puppeteer-sharp-azure-functions 使用。
  92. //string url = "https://teammodelos.blob.core.chinacloudapi.cn/0-public/pie-borderRadius.html";
  93. try
  94. {
  95. var bfOptions = new BrowserFetcherOptions();
  96. if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
  97. {
  98. string dir = "/app";
  99. if (!Directory.Exists(dir))
  100. {
  101. Directory.CreateDirectory(dir);
  102. }
  103. bfOptions.Path = dir;
  104. }
  105. var bf = new BrowserFetcher(bfOptions);
  106. var revisionInfo = bf.DownloadAsync(BrowserFetcher.DefaultChromiumRevision).Result;
  107. string BrowserExecutablePath = revisionInfo.ExecutablePath;
  108. var browser = await Puppeteer.LaunchAsync(new LaunchOptions
  109. {
  110. ExecutablePath = BrowserExecutablePath,
  111. Headless = true,
  112. Args = new string[] { "--no-sandbox", "--disable-setuid-sandbox" }
  113. });
  114. var page = await browser.NewPageAsync();
  115. bool fullPage = true;
  116. await page.SetViewportAsync(new ViewPortOptions
  117. {
  118. Width = screenshot.width,
  119. Height = screenshot.height
  120. });;
  121. fullPage = false;
  122. await page.GoToAsync(System.Web.HttpUtility.UrlDecode(screenshot.url));
  123. Thread.Sleep(screenshot.delay);
  124. string base64 = await page.ScreenshotBase64Async(new ScreenshotOptions { FullPage = fullPage, BurstMode = true });
  125. //关闭浏览器
  126. await browser.CloseAsync();
  127. await browser.DisposeAsync();
  128. return Ok(new { url = base64 ,type= "data:image/png;base64," });
  129. }
  130. catch (Exception ex)
  131. {
  132. return BadRequest($"{ex.Message}\n{ex.StackTrace}");
  133. }
  134. }
  135. [HttpPost("screenshot-pdf")]
  136. public async Task<IActionResult> ScreenshotPngs(ScreenshotDto screenshot)
  137. {
  138. //W3C School教程 https://www.w3cschool.cn/puppeteer/puppeteer-rip537tj.html
  139. // 进入容器的命令 docker exec -it f9e27d175498 /bin/bash
  140. //依赖包 https://blog.csdn.net/weixin_45447477/article/details/115188938
  141. //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
  142. //解决ubuntu18上使用puppeteer https://blog.csdn.net/qq_42414062/article/details/114539378
  143. //https://www.hardkoded.com/blog/running-puppeteer-sharp-azure-functions 使用。
  144. //string url = "https://teammodelos.blob.core.chinacloudapi.cn/0-public/pie-borderRadius.html";
  145. try
  146. {
  147. var bfOptions = new BrowserFetcherOptions();
  148. if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
  149. {
  150. string dir = "/app";
  151. if (!Directory.Exists(dir))
  152. {
  153. Directory.CreateDirectory(dir);
  154. }
  155. bfOptions.Path = dir;
  156. }
  157. var bf = new BrowserFetcher(bfOptions);
  158. var revisionInfo = bf.DownloadAsync(BrowserFetcher.DefaultChromiumRevision).Result;
  159. string BrowserExecutablePath = revisionInfo.ExecutablePath;
  160. var browser = await Puppeteer.LaunchAsync(new LaunchOptions
  161. {
  162. ExecutablePath = BrowserExecutablePath,
  163. Headless = true,
  164. Args = new string[] { "--no-sandbox", "--disable-setuid-sandbox" }
  165. });
  166. ViewPortOptions viewPortOptions = new ViewPortOptions
  167. {
  168. Width = screenshot.width,
  169. Height = screenshot.height
  170. };
  171. PdfOptions pdfOptions = new PdfOptions { DisplayHeaderFooter = true, PreferCSSPageSize =true};
  172. //ScreenshotOptions screenshotOptions= new ScreenshotOptions { FullPage = fullPage, BurstMode = true };
  173. int pagesize = 20;
  174. if (screenshot.urls.Count <= pagesize)
  175. {
  176. await PageToPdfStream(screenshot.urls,screenshot.fileNameKey,screenshot.cnt,browser,viewPortOptions,pdfOptions);
  177. }
  178. else
  179. {
  180. List<Task> tasks = new List<Task>();
  181. int pages = (screenshot.urls.Count + pagesize) / pagesize;
  182. for (int i = 0; i < pages; i++)
  183. {
  184. var lists = screenshot.urls.Skip((i) * pagesize).Take(pagesize).ToList();
  185. tasks.Add(PageToPdfStream(lists, screenshot.fileNameKey, screenshot.cnt, browser, viewPortOptions, pdfOptions));
  186. }
  187. await Task.WhenAll(tasks);
  188. }
  189. //browser.NewPageAsync();
  190. //foreach (var url in screenshot.urls) {
  191. // var page = await browser.NewPageAsync();
  192. // await page.SetViewportAsync(viewPortOptions);
  193. // string file = $"E://pdfs//{Guid.NewGuid().ToString()}.pdf";
  194. // var respons = await page.GoToAsync(System.Web.HttpUtility.UrlDecode(url), WaitUntilNavigation.Networkidle2);
  195. // if (respons.Ok)
  196. // {
  197. // await page.PdfAsync(file, pdfOptions);
  198. // // string base64 = await page.ScreenshotBase64Async(screenshotOptions);
  199. // }
  200. //}
  201. //关闭浏览器
  202. await browser.CloseAsync();
  203. await browser.DisposeAsync();
  204. return Ok();
  205. }
  206. catch (Exception ex)
  207. {
  208. return BadRequest($"{ex.Message}\n{ex.StackTrace}");
  209. }
  210. }
  211. private async Task PageToPdfStream(List<string> urls,string fileNameKey,string cnt , Browser browser, ViewPortOptions viewPortOptions, PdfOptions pdfOptions)
  212. {
  213. List<Task<Page>> pages = new List<Task<Page>>();
  214. urls.ForEach(x => {
  215. pages.Add(browser.NewPageAsync());
  216. });
  217. var page_tasks = await Task.WhenAll(pages);
  218. List<Task<Response>> responses = new List<Task<Response>>();
  219. page_tasks.ToList().ForEach(x => {
  220. x.SetViewportAsync(viewPortOptions);
  221. });
  222. for (int i = 0; i < urls.Count; i++)
  223. {
  224. responses.Add(page_tasks[i].GoToAsync(System.Web.HttpUtility.UrlDecode(urls[i]), WaitUntilNavigation.Networkidle2));
  225. }
  226. var responses_tasks = await Task.WhenAll(responses);
  227. //List<Task<Stream>> streams = new List<Task<Stream>>();
  228. List<Task> tasks = new List<Task>();
  229. List<Task<string>> uploads = new List<Task<string>>();
  230. foreach (var page_task in page_tasks) {
  231. string url = page_task.Url;
  232. string[] paths= HttpUtility.UrlDecode(url).Split("/");
  233. var reg = $"(?<=\\b{fileNameKey}=)[^&]*";
  234. Regex regex = new Regex(reg);
  235. string decode = HttpUtility.UrlDecode(url);
  236. Match match = Regex.Match(decode, reg);
  237. string id = "";
  238. while (match.Success)
  239. {
  240. id = id + $"{match.Value}";
  241. match = match.NextMatch();
  242. }
  243. //需要解析参数。paths[paths.Length-1]
  244. Stream stream = await page_task.PdfStreamAsync(pdfOptions);
  245. if (string.IsNullOrWhiteSpace(cnt))
  246. {
  247. uploads.Add(_azureStorage.GetBlobContainerClient("teammodelos").UploadFileByContainer(stream, "artreport", $"{id}.pdf", true));
  248. }
  249. else {
  250. uploads.Add(_azureStorage.GetBlobContainerClient(cnt).UploadFileByContainer(stream, "artreport", $"{id}.pdf", true));
  251. }
  252. }
  253. string[] uploadUrls= await Task.WhenAll(uploads);
  254. page_tasks.ToList().ForEach(x => {
  255. tasks.Add(x.DisposeAsync().AsTask());
  256. });
  257. await Task.WhenAll(tasks);
  258. }
  259. private async Task PageToPdf(List<string> urls, Browser browser, ViewPortOptions viewPortOptions, PdfOptions pdfOptions)
  260. {
  261. List<Task<Page>> pages = new List<Task<Page>>();
  262. urls.ForEach(x => {
  263. pages.Add(browser.NewPageAsync());
  264. });
  265. var page_tasks = await Task.WhenAll(pages);
  266. List<Task<Response>> responses = new List<Task<Response>>();
  267. page_tasks.ToList().ForEach(x => {
  268. x.SetViewportAsync(viewPortOptions);
  269. });
  270. for (int i = 0; i < urls.Count; i++)
  271. {
  272. responses.Add(page_tasks[i].GoToAsync(System.Web.HttpUtility.UrlDecode(urls[i]), WaitUntilNavigation.Networkidle2));
  273. }
  274. var responses_tasks = await Task.WhenAll(responses);
  275. List<Task> tasks = new List<Task>();
  276. page_tasks.ToList().ForEach(x => {
  277. string file = $"{Guid.NewGuid()}.pdf";
  278. tasks.Add(x.PdfAsync(file, pdfOptions));
  279. });
  280. await Task.WhenAll(tasks);
  281. tasks.Clear();
  282. page_tasks.ToList().ForEach(x => {
  283. tasks.Add(x.DisposeAsync().AsTask());
  284. });
  285. await Task.WhenAll(tasks);
  286. }
  287. }
  288. }
  289. /// <summary>
  290. /// 网页截图参数
  291. /// </summary>
  292. public class ScreenshotDto
  293. {
  294. public int width { get; set; } = 1920;
  295. public int height { get; set; } = 1080;
  296. public string? url { get; set; }
  297. /// <summary>
  298. /// 批量地址
  299. /// </summary>
  300. public List<string> urls { get; set; } = new List<string>();
  301. /// <summary>
  302. /// 提取参数的唯一id作为文件名
  303. /// </summary>
  304. public string? fileNameKey { get; set; }
  305. /// <summary>
  306. /// 存在哪个容器里
  307. /// </summary>
  308. public string? cnt { get; set; }
  309. public int delay { get; set; }
  310. }