【问题标题】:Save Razor Component as JPG/PNG/PDF via Download Link通过下载链接将 Razor 组件保存为 JPG/PNG/PDF
【发布时间】:2021-06-08 01:12:40
【问题描述】:

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

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

【问题讨论】:

  • 我认为答案可能与 this 有关,尽管我没有与 Blazor Wasm 合作过,所以也许有更 Blazor-y 的方式来做到这一点。
  • @Llama 这也是我的想法。我看到了,但不确定是否有更技术上正确的方法可以通过 Blazor 来做到这一点
  • 这肯定需要 javascript 解决方案。如果你想包含 SVG 图像,你真的应该这样做,那么如果你找到解决方案,你就是一个比我更好的人。我的建议是针对 SPECIFIC 控件,定义某种帮助类来输出 .pdf 文件或以足够接近的格式输出的东西。但这并不能真正回答您的问题。

标签: c# image razor blazor


【解决方案1】:

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

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

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

然后添加一个从 C# 调用的 JS 函数,这样我们就可以在 _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标签内,否则不能单独选择,例如父级中的好孩子:

Parent.razor

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

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

@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函数,将查询选择器设置为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 一样注入。对于解析 URI,我们可以使用 QueryHelpers 类。 最终代码将如下所示:

@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;

【讨论】:

    猜你喜欢
    • 2020-07-18
    • 1970-01-01
    • 2017-10-07
    • 1970-01-01
    • 2012-06-27
    • 1970-01-01
    • 2017-03-01
    • 2013-05-10
    • 2020-09-16
    相关资源
    最近更新 更多