c# - 通过下载链接将 Razor 组件保存为 JPG/PNG/PDF

标签 c# image razor blazor

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

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

最佳答案

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

下载它并将它放在您的 wwwroot 文件夹中,然后通过在 body 标记的末尾添加以下代码将其包含在 _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>

这将自动从元素截屏并下载它,同时返回它的 URL。注意组件必须在带有id的div标签内,否则不能单独选中,例如good child in parent:

Parent.razor

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

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

@inject IJSRuntime JS

接下来,一个做所有事情的函数:

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

此函数将返回从 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 将如下所示:http://localhost:5001/page?img=true
为此,我们需要使用 NavigationManager 获取 URI,它可以像 IJSRuntime 一样被注入(inject)。为了解析 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有背景,而你想让它出现在图片中,你需要在包含的div的CSS规则中添加background: inherit;子组件。

关于c# - 通过下载链接将 Razor 组件保存为 JPG/PNG/PDF,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67880262/

相关文章:

javascript - 如何使用 ASP.NET MVC 显示基于单选按钮的内容?

C# pinvoke 具有 union 和数组的结构

c# - 简单类型的只读自动属性 ​​: Initializer VS Expression Body Getter

c# - 如何在 Internet Explorer 或 .NET Web 浏览器控件中阻止 iframe

java - 用于模板匹配的 OpenCV 读取图像

PHP不打印MySQL图像

c# - ASP.NET MVC 网络 API : application/xml PUT request body is null on server side

image - 计算旋转2D图像的平移值和旋转角度

c# - Razor 语法 : Trouble figuring out nested statements and variable scope

c# - 在 asp.net mvc5 中使用自定义登录进行表单例份验证