跳至页脚内容
使用IRONPDF

生成每月账户报表(PDF 文档)

静态和手动文档交付的问题

IronPDF首页 当投保人打电话要求他们当前的声明页,或银行客户打开支持票以请求三个月的对账单时,要求最终会出现在工作人员的桌面上。 那个人查询系统,格式化文档,并通过电子邮件发回,有时是当天,有时是48小时后。 在成千上万的客户中,该工作流程无法扩展,并且每个请求都需要一个自助门户来是不必要的。

组织的替代方案往往是以一个问题换另一个问题。 每晚为每个客户生成PDF会产生大量可能永远不会下载的文档,每次基础数据改变,这些文档都会开始过时。 一个更新了付款方式或改变了保险等级的客户可能会下载一个不再反映其账户的PDF,并且无论如何会打电话支持。

第三方文档生成API提供按需渲染,但每个请求增加的成本直接随着门户流量而增长,加上在用户操作关键路径中的网络依赖。 每月发送200,000份声明下载的银行门户不想在计量API上实现这一点。

用户的期望很简单:一个"下载PDF"按钮立即生成正确的文档。 下载声明页的投保人,一个下载最新账单的公共事业客户,一个导出当前订阅报告的SaaS管理员,一个下载通话详情记录的电信客户,他们都期待即时结果,而不是排队的请求。

解决方案:在API端点内按需渲染

IronPDF让.NET web应用程序在控制器动作或最小API端点内部按需生成PDF文件。 当用户点击"下载PDF"时,服务器从数据库中提取他们的实时数据,填充HTML模板,ChromePdfRenderer在内存中渲染PDF内容。 响应直接将文件流式传输到浏览器作为下载,无需预生成,无陈旧文件,无支持队列。

该文档反映了请求的精确时刻用户的账户数据。渲染在现有的.NET应用程序内部运行,作为一个单一的NuGet包,不需要外部进程,无需调用文档API,无需保持任何附属服务活着,也无需跟踪每次下载成本。

由于这是一个用于PDF工作流的C# NuGet库,您可以轻松地通过NuGet包管理器安装IronPDF。 具有包括PDF生成、修改新旧PDF文件的工具、应用数字签名和密码等安全措施等关键功能,IronPDF是一项非常适合添加到您的项目中的好工具。

实际应用示例

1. 用户在门户上点击"下载PDF文档"

该按钮触发一个HTTP GET到API端点,传递文档类型和用户选择的任何参数,如账单期间、保单号、日期范围。 端点在进行任何其他操作之前对请求进行身份验证和授权,验证身份和特定文档的权限。

授权在这里不是可选的。 客户请求自己的声明可以;客户请求另一个客户的则不行。 端点必须在查询任何数据之前验证认证用户是否有权请求他们正在请求的文档ID或参数。

示例门户视图

示例按钮生成我们的PDF

2. 实时数据填充包含HTML内容的模板

动作方法查询用户当前数据的数据库,例如账户详细信息、项目行、余额、保单条款、覆盖范围选择或使用指标,并填充特定于文档类型的HTML模板。 模板包括客户的名称和账户号码、文档日期、所有相关项目行、公司品牌和页脚中的任何必需法律免责声明文本。

不同的HTML模板处理每种文档类型:保单概要、账户声明、付款收据,均通过相同的控制器和生成管道渲染。

3. ChromePdfRenderer将HTML转换为PDF并返回文件

using IronPdf;

[HttpGet("portal/documents/statement")]
[Authorize]
public IActionResult DownloadStatement([FromQuery] string period)
{
    var userId = User.GetUserId();
    var data = _accountService.GetStatementData(userId, period);

    string html = $@"
        <h1>Account Statement — {data.Period}</h1>
        <p><strong>Account:</strong> {data.AccountNumber}</p>
        <p><strong>Name:</strong> {data.CustomerName}</p>
        {data.LineItemsHtml}
        <p><strong>Closing Balance:</strong> {data.ClosingBalance:C}</p>";

    var renderer = new ChromePdfRenderer();

    renderer.RenderingOptions.MarginTop = 20;

    renderer.RenderingOptions.MarginBottom = 20;

    PdfDocument pdf = renderer.RenderHtmlAsPdf(html);

    return File(pdf.BinaryData, "application/pdf",
        $"Statement-{data.Period}-{data.AccountNumber}.pdf");

}
using IronPdf;

[HttpGet("portal/documents/statement")]
[Authorize]
public IActionResult DownloadStatement([FromQuery] string period)
{
    var userId = User.GetUserId();
    var data = _accountService.GetStatementData(userId, period);

    string html = $@"
        <h1>Account Statement — {data.Period}</h1>
        <p><strong>Account:</strong> {data.AccountNumber}</p>
        <p><strong>Name:</strong> {data.CustomerName}</p>
        {data.LineItemsHtml}
        <p><strong>Closing Balance:</strong> {data.ClosingBalance:C}</p>";

    var renderer = new ChromePdfRenderer();

    renderer.RenderingOptions.MarginTop = 20;

    renderer.RenderingOptions.MarginBottom = 20;

    PdfDocument pdf = renderer.RenderHtmlAsPdf(html);

    return File(pdf.BinaryData, "application/pdf",
        $"Statement-{data.Period}-{data.AccountNumber}.pdf");

}
Imports IronPdf
Imports Microsoft.AspNetCore.Mvc

<HttpGet("portal/documents/statement")>
<Authorize>
Public Function DownloadStatement(<FromQuery> period As String) As IActionResult
    Dim userId = User.GetUserId()
    Dim data = _accountService.GetStatementData(userId, period)

    Dim html As String = $"
        <h1>Account Statement — {data.Period}</h1>
        <p><strong>Account:</strong> {data.AccountNumber}</p>
        <p><strong>Name:</strong> {data.CustomerName}</p>
        {data.LineItemsHtml}
        <p><strong>Closing Balance:</strong> {data.ClosingBalance:C}</p>"

    Dim renderer As New ChromePdfRenderer()

    renderer.RenderingOptions.MarginTop = 20

    renderer.RenderingOptions.MarginBottom = 20

    Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(html)

    Return File(pdf.BinaryData, "application/pdf", $"Statement-{data.Period}-{data.AccountNumber}.pdf")
End Function
$vbLabelText   $csharpLabel

示例输出PDF文件

IronPDF示例输出 File()带有文件名的情况下自动设置Content-Disposition:attachment,提示浏览器保存文件而不是尝试内联渲染。PDF从不写入磁盘,而是在内存中生成并直接流式传输到响应中。

4. 可选缓存以重复下载

对于基础数据变动不频繁的文档类型,例如只在续约时更新的保单声明页面,缓存层避免多次重新渲染同一个PDF:

var dataHash = _accountService.GetStatementDataHash(userId, period);

string cacheKey = $"statement/{userId}/{period}/{dataHash}";

byte[] pdfBytes = await _cache.GetAsync(cacheKey);

if (pdfBytes == null)
{
    var renderer = new ChromePdfRenderer();
    PdfDocument pdf = renderer.RenderHtmlAsPdf(html);

    pdfBytes = pdf.BinaryData;

    await _cache.SetAsync(cacheKey, pdfBytes,
        new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24)
        });
}

return File(pdfBytes, "application/pdf", $"Statement-{period}.pdf");
var dataHash = _accountService.GetStatementDataHash(userId, period);

string cacheKey = $"statement/{userId}/{period}/{dataHash}";

byte[] pdfBytes = await _cache.GetAsync(cacheKey);

if (pdfBytes == null)
{
    var renderer = new ChromePdfRenderer();
    PdfDocument pdf = renderer.RenderHtmlAsPdf(html);

    pdfBytes = pdf.BinaryData;

    await _cache.SetAsync(cacheKey, pdfBytes,
        new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24)
        });
}

return File(pdfBytes, "application/pdf", $"Statement-{period}.pdf");
Imports System
Imports System.Threading.Tasks
Imports IronPdf

Dim dataHash = _accountService.GetStatementDataHash(userId, period)

Dim cacheKey As String = $"statement/{userId}/{period}/{dataHash}"

Dim pdfBytes As Byte() = Await _cache.GetAsync(cacheKey)

If pdfBytes Is Nothing Then
    Dim renderer As New ChromePdfRenderer()
    Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(html)

    pdfBytes = pdf.BinaryData

    Await _cache.SetAsync(cacheKey, pdfBytes, New DistributedCacheEntryOptions With {
        .AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24)
    })
End If

Return File(pdfBytes, "application/pdf", $"Statement-{period}.pdf")
$vbLabelText   $csharpLabel

缓存键纳入一组基础数据的哈希值,而不仅仅是用户ID和周期。 如果数据发生变化,如收到延付款、应用信用、哈希值变化、缓存未命中,下一次下载从当前数据重新渲染。 客户永远不会下载过时文件。

实际好处

始终最新。 文件在请求时刻由实时数据生成。数据更改与文档内容之间没有同步滞后,更新账户信息的客户下载的PDF即刻反映它。

零支持成本。 能够下载自己保单、声明或信件的客户不会开启请求票。 下载按钮取代了每个请求耗时的支持工作流程。

无存储膨胀。 PDF按需生成并流式传输到浏览器。 无须为每个账单周期的每位顾客预生成并存储文档,存储成本不随客户基数增长。

快速响应时间。 ChromePdfRenderer以毫秒级时间生产一个常规门户文档:一页声明、保单概要、付款收据。 用户体验到几乎即时的下载,而不是一个进度指示器。

品牌一致性。 每种文档类型均有经过批准的HTMLCSS模板。 无论请求使用哪个端点处理,或在何时生成,每下载的PDF都带有相同的logo、排版、布局和法律免责声明。

无每文件成本。 渲染在Web应用程序内部进行。 无第三方API计量,无基于使用定价,无随门户流量增长的基础设施账单项目。

结语

按需PDF下载是一个影响不成比例的小特性。 它完全消除了一个类别的支持请求,消除了用户依据过时文档操作的风险,并取代了需要几百毫秒的手动传送流程。

实现适合包含在一个控制器动作、一个数据库查询、一个HTML模板、一个渲染调用和一个文件响应中。 IronPDF处理C#中PDF生成的完整生命周期在ironpdf.com,从渲染HTML模板到保存、流处理和操作文档。 30天免费试用给予您足够时间构建和测试按需下载端点以适应您的门户和数据。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我