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
相关产品推荐
相关产品推荐

