You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular+TS+.NET6 Web API实现后端生成PDF并前端下载方案问询

实现Angular + .NET 6 Web API的PDF生成与下载功能

由于.NET 6本身没有内置的HTML转PDF功能,我们需要借助开源库来实现。这里选择PuppeteerSharp(基于无头Chrome),它能准确渲染复杂HTML,适合生成长报表,且完全免费开源。


后端(.NET 6 Web API)实现步骤

1. 安装PuppeteerSharp依赖

通过NuGet包管理器安装:

Install-Package PuppeteerSharp

或使用.NET CLI:

dotnet add package PuppeteerSharp

2. 优化控制器代码

修改DownloadPDF方法,完成HTML模板变量替换、PDF生成并返回流:

using PuppeteerSharp;
using Microsoft.EntityFrameworkCore;

[ApiController]
[Route("api/[controller]")]
public class ReportController : ControllerBase
{
    private readonly YourDbContext _context;
    private readonly IBrowser _browser;

    // 注入浏览器实例(单例,避免重复下载Chrome)
    public ReportController(YourDbContext context, IBrowser browser)
    {
        _context = context;
        _browser = browser;
    }

    [HttpGet]
    [Route("DownloadPDF")]
    public async Task<IActionResult> DownloadPDF(Guid userId)
    {
        // 获取用户数据
        var user = await _context.Users.FirstAsync(p => p.id == userId);
        string firstName = user.FirstName;
        string age = user.Age.ToString(); // 假设用户有Age字段

        // 定义HTML模板,替换变量
        string htmlTemplate = @"
        <html>
           <head>
               <style>
                   body { font-family: Arial, sans-serif; padding: 20px; }
                   .section { margin-bottom: 15px; }
               </style>
           </head>
           <body>
              <div class='section'>My name is {FirstName}</div>
              <div class='section'>My age is {Age}</div>
           </body>
        </html>";
        string renderedHtml = htmlTemplate.Replace("{FirstName}", firstName)
                                          .Replace("{Age}", age);

        // 使用无头Chrome渲染HTML为PDF
        using var page = await _browser.NewPageAsync();
        await page.SetContentAsync(renderedHtml);
        
        var pdfStream = new MemoryStream();
        var pdfBytes = await page.PdfStreamAsync(new PdfOptions
        {
            Format = PaperFormat.A4,
            MarginOptions = new MarginOptions { Top = "1cm", Bottom = "1cm", Left = "1cm", Right = "1cm" },
            PrintBackground = true // 渲染背景样式(如果需要)
        });
        
        await pdfBytes.CopyToAsync(pdfStream);
        pdfStream.Position = 0; // 重置流位置到开头

        return File(pdfStream, "application/pdf", $"UserReport_{userId}.pdf");
    }
}

3. 注册单例浏览器实例

在Program.cs中添加代码,确保浏览器只初始化一次:

using PuppeteerSharp;

var builder = WebApplication.CreateBuilder(args);

// 注册Puppeteer浏览器单例
builder.Services.AddSingleton(async () =>
{
    await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision);
    return await Puppeteer.LaunchAsync(new LaunchOptions 
    { 
        Headless = true,
        Args = new[] { "--no-sandbox", "--disable-setuid-sandbox" } // Linux环境可能需要的参数
    });
});

// 注册DbContext等其他服务
builder.Services.AddDbContext<YourDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// ...其他配置

var app = builder.Build();

// ...中间件配置

app.Run();

前端(Angular)实现步骤

1. 完善组件TS代码

在组件的download方法中,请求API获取Blob并触发下载:

import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';

@Component({
  selector: 'app-pdf-download',
  templateUrl: './pdf-download.component.html',
  styleUrls: ['./pdf-download.component.css']
})
export class PdfDownloadComponent {
  constructor(private http: HttpClient) { }

  download(userId: string) {
    // 发起GET请求,指定响应类型为Blob
    this.http.get(`/api/Report/DownloadPDF?userId=${userId}`, { responseType: 'blob' })
      .subscribe({
        next: (blob) => {
          // 创建临时URL并触发下载
          const blobUrl = window.URL.createObjectURL(blob);
          const downloadLink = document.createElement('a');
          downloadLink.href = blobUrl;
          downloadLink.download = `UserReport_${userId}.pdf`;
          downloadLink.click();
          // 释放URL资源
          window.URL.revokeObjectURL(blobUrl);
        },
        error: (err) => {
          console.error('下载失败:', err);
          // 可添加错误提示逻辑
        }
      });
  }
}

2. 确保HTML模板正确

你的现有HTML代码无需修改,只需确保API路由与后端一致:

<a (click)="download('f58c8036-915a-4c39-bbb8-352e21eae49c')">Download pdf</a>

注意事项

  • 跨域问题:如果Angular前端与API不在同一域名,需在后端配置CORS。
  • 性能优化:生产环境中,避免每次请求都初始化浏览器(已通过单例解决),同时可考虑缓存模板或PDF结果。
  • HTML样式:确保HTML模板中的样式兼容无头Chrome,避免使用仅在特定浏览器生效的CSS特性。

内容的提问来源于stack exchange,提问作者AllRight

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 14:35:26