通过下载 Link 将 Razor 组件另存为 JPG/PNG/PDF

Save Razor Component as JPG/PNG/PDF via Download Link

我正在寻找一种创建 link 的方法,它将创建 Razor 组件的屏幕截图并通过 Blazor 服务器应用程序将其下载为 JPG、PNG 或 PDF。理想情况下,它 包含 Razor 组件和所有子组件,但没有父组件,图像将具有浏览器上显示的当前状态的精确外观。

唯一类似的事情是捕获 HTML 画布,但由于我对 Blazor 还很陌生,所以我不确定如何应用它,并且想知道是否有办法实现类似的事情通过 C#。任何建议,将不胜感激。谢谢:)

我认为单独使用 C# 实现这一目标并不容易,因为解决该问题的最佳方法是将 HTML 数据转换为 canvas 并将其导出为图像这是一个艰难的过程,但在 JS 中有可用的库。 怎么做:
我将使用这个库,因为它似乎是最简单的:html2canvas
这是它的 JS 文件的 link:html2canvas.js

下载它并将其放在您的 wwwroot 文件夹中,然后通过在正文标记的末尾添加以下内容将其包含在 _host.cshtml 中:

<script src="html2canvas.js"></script>

然后添加一个 JS 函数以从 C# 调用,这样我们就可以通过在 _host.cshtml 中的先前代码之后添加它来用 C# 编写偶数处理程序:

<script>
    window.takeScreenshot = async function(id) {
        var img = "";
        await html2canvas(document.querySelector("#" + id)).then(canvas => img = canvas.toDataURL("image/png"));
        var d = document.createElement("a");
        d.href = img;
        d.download = "image.png";
        d.click();
        return img;
    }
</script>

这将自动从元素截屏并下载,return URL。请注意,该组件必须位于带有 id 的 div 标签内,否则,您不能单独 select 它,例如 good child in parent:

Parent.razor

<div id="child"></div>
    <Child />
</div>

要使用此功能,请使用JsInterop class。简单地说,通过在文件顶部添加以下内容,将它注入(基本上包含)到您需要此功能的剃刀组件中:

@inject IJSRuntime JS

接下来,执行所有操作的函数:

@inject IJSRuntime JS
@code {
    public async System.Threading.Tasks.Task<string> TakeImage(string id)
    {
        return await JS.InvokeAsync<string>("takeScreenshot", id);
    }
}

此函数将 return 从 id 参数指定的元素中获取的图像数据 URL。 使用按钮拍照的示例用法:

@page "/screenshot"
@inject IJSRuntime JS
@code {
    string image_url = "";
    string child_id = "child";
    public async System.Threading.Tasks.Task<string> TakeImage(string id)
    {
        return await JS.InvokeAsync<string>("takeScreenshot", id);
    }
    public async System.Threading.Tasks.Task ButtonHandler()
    {
        image_url = await TakeImage(child_id);
    }
}
<button @onclick="ButtonHandler">Take image</button>
<h3>Actual component:</h3>
<div id=@child_id>
    <ChildComponent />
</div>
<h3>Image:</h3>
<img src=@image_url />
<br />
<br />
<br />
<p>URL: @image_url</p>

按下按钮将下载图像,显示它,显示原始 URL,并将 URL 保存到变量 image_url。 您可以通过将 @using System.Threading.Tasks 添加到 _Imports.razor 文件来将 System.Threading.Tasks.Task 缩短为 Task。 您可以通过删除 JS 函数中的这 4 行来删除自动下载功能:

var d = document.createElement("a");
d.href = img;
d.download = "image.png";
d.click();

如果您想自动拍摄整个页面的图像并在没有任何用户交互的情况下下载:

修改JS函数,设置query selector到body,去掉id参数:

<script>
    window.takeScreenshot = async function() {
        var img = "";
        await html2canvas(document.querySelector("body")).then(canvas => img = canvas.toDataURL("image/png"));
        var d = document.createElement("a");
        d.href = img;
        d.download = "image.png";
        d.click();
        return img;
    }
</script

在文档加载时将函数设置为 运行:

@inject IJSRuntime JS
@page "/component"
@code {
    string image_url = "";    
    public async System.Threading.Tasks.Task<string> TakeImage()
    {
        return await JS.InvokeAsync<string>("takeScreenshot");
    }
    protected override async System.Threading.Tasks.Task OnAfterRenderAsync(bool firstRender)
    {
        if(firstRender) //Ensure this is the page load, not any page rerender
        {
            image_url = await TakeImage();
        }
    }
}

特殊URL自动下载图片:

生成一个 URL,它在加载时会像前一部分一样下载图像,但只有在加载特殊 URL 时才会加载,而不是普通页面 URL.
为此,我将使用查询字符串,所以特殊的 URL 将像这样:http://localhost:5001/page?img=true
为此,我们需要使用 NavigationManager 获取 URI,它可以像 IJSRuntime 一样被注入。为了解析 URI,我们可以使用 QueryHelpers class。 最终代码将如下所示:

@inject IJSRuntime JS
@inject NavigationManager Nav
@page "/component"
@code {
    string image_url = "";    
    public async System.Threading.Tasks.Task<string> TakeImage()
    {
        return await JS.InvokeAsync<string>("takeScreenshot");
    }
    protected override async System.Threading.Tasks.Task OnAfterRenderAsync(bool firstRender)
    {
        if(firstRender) //Ensure this is the page load, not any page rerender
        {
            var uri = Nav.ToAbsoluteUri(Nav.Uri);
            if (Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query).TryGetValue("img", out var isImg))
            {
                if (System.Convert.ToBoolean(isImg.ToString()))
                {
                    image_url = await TakeImage();
                }
            }
        }
    }
}

现在您可以将?img=true添加到组件的URL中,您将获得它的屏幕截图。

注意,如果div的body/parent有背景,而你想让它出现在图片中,需要在[=78=中添加background: inherit; ] 包含子组件的 div 规则。