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;
using TEAMModelOS.SDK.DI;
namespace HTEX.Screen.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).UploadFileByContainerBName(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).UploadFileByContainerBName(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 });
}
[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));
await Task.Delay(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).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();
}
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);
}
}
}