有些需求比如生成的报告,需要添加Echarts图表,作为后台任务,echarts也需要后台执行,所以需要用到无头浏览器,这个没什么可说的。下面说下实践步骤,可直接用到项目里。
环境:Windows 11, Selenium.WebDriver(4.5.1), chromedriver.exe(版本107.0.5304.62), google浏览器(版本 107.0.5304.107),.Net 6。
注意chromedriver.exe和google浏览器的版本问题,要相互兼容,官网有相关说明。
以共用接口为例,创建一个后台生成echarts图片的接口项目:
1. 添加Selenium.WebDriver(4.5.1) package,并将chromedriver.exe复制到项目里(根目录),属性设置 复制到输出目录 为 如果较新则复制,这样发布的时候会跟随应用程序一起发布。
2. 创建一个视图模型控制器,添加代码
/// <summary>
/// 渲染echart图形
/// </summary>
/// <returns>IActionResult</returns>
public IActionResult GenImg()
{
return View();
}
3. 添加视图,添加如下代码, 注意引用echarts.js
@{
Layout = null;
}
<body style="margin:0px;">
<div id="chartContainer" style="width: 1102px; height: 640px; padding: 0px; margin: 0px;"></div>
</body>
<script src="~/echarts.min.js"></script>
<script>
</script>
4. 创建API控制器,添加如下代码:
/// <summary>
/// 导出图表图片,返回一个 base64 的 URL,可以设置为Image的src。
/// </summary>
/// <param name="option">echarts option</param>
/// <returns>图片base64字符串</returns>
[HttpPost]
public string GetBase64String([FromBody] object option)
{
return GenBase64String(option);
}
/// <summary>
/// 生成base64字符串
/// </summary>
/// <param name="option">echarts option</param>
/// <returns>图片base64字符串</returns>
private string GenBase64String(object option)
{
var service = ChromeDriverService.CreateDefaultService();
var options = new ChromeOptions();
options.AddArguments("--headless");
options.AddArgument("--disable-gpu");
options.AddArgument("--no-sandbox");
var commandTimeout = TimeSpan.FromMinutes(1);
using (ChromeDriver driver = new ChromeDriver(service, options, commandTimeout))
{
driver.Navigate().GoToUrl(Url.Action("GenImg", "Echarts", new { area = "Demo" }, Request.Scheme, Request.Host.Value));
IJavaScriptExecutor scriptExecutor = driver;
string echartsJS =
"var myChart = echarts.init(document.getElementById('chartContainer'));" +
"myChart.on('finished',()=>{resolve(myChart.getDataURL({type:'png'}));});" +
$"var option = {JsonConvert.SerializeObject(option)};myChart.setOption(option);";
string promiseJS = "let promise = new Promise((resolve,reject)=>{" +
echartsJS + "});" +
"var base64='';await promise.then((res)=>{base64 = res;});return base64;";
return scriptExecutor.ExecuteScript(promiseJS).ToString();
}
}
解释一下,参数option就是echarts生成图表的option配置(注意设置animation=false,backgroundColor = "#fff")。核心方法是GenBase64String,这里面的代码不管是API还是其他的后台服务,都可以用。
Url.Action("GenImg", "Echarts", new { area = "Demo" }, Request.Scheme, Request.Host.Value)
这行代码,如果你没有新建文件夹作为Area, new { area = "Demo" }便可以不要。
里面用到了Promise,可以保证图片是渲染完成后再获取base64编码,这个是为了避免数据量巨大的时候,丢失数据,比如散点图,如果渲染没有完成,就执行到了获取base64编码的代码,点数丢失很严重。
最后也要避免将ChromeDriver作为单例使用,除非你能保证所有的请求或者调用按顺序执行并且都已经生成图片。像我写成接口,也许同时会有很多请求进来,如果是单例,driver.Navigate().GoToUrl可能会引发不可预测的结果。