
如何在 .NET Maui 中查看 PDF 文件
从Rotativa迁移到IronPDF可解决关键的安全漏洞,同时实现 PDF 生成工作流程的现代化。 本指南提供了一个完整的、循序渐进的迁移路径,消除了废弃的 wkhtmltopdf 依赖性,实现了对现代 CSS 和 JavaScript 的支持,并提供了超越 ASP.NET MVC 的跨平台兼容性。
为何从Rotativa迁移到 IronPDF.
了解 Rotativa.
Rotativa 长期以来一直是开发人员在 C# 中生成 PDF 的热门选择。 它利用wkhtmltopdf工具将HTML内容转换为PDF格式。Rotativa是专为 ASP.NET MVC 应用程序设计的开源库。 然而,虽然Rotativa吸引了大量受众,但它对过时技术栈的依赖也带来了挑战,这些挑战可能不是每个开发人员都能立即意识到的。
Rotativa的核心提供了一种简单的方法,将PDF生成集成到ASP.NET MVC项目中,利用wkhtmltopdf实现其后端功能。
重要安全咨询
Rotativa封装了wkhtmltopdf,它存在严重的未修补安全漏洞。
| 属性 | 价值 |
|---|---|
| CVE ID | CVE-2022-35583 |
| 严重性 | 关键 (9.8/10) |
| 攻击向量 | 网络 |
| 状态 | 无固定版本(wkhtmltopdf二进制文件已归档) |
| 受影响 | 所有Rotativa版本均捆绑wkhtmltopdf 0.12.6 |
wkhtmltopdf GitHub仓库于2023年1月2日归档(只读)。维护者的状态页指出该项目依赖于Qt 4(自2015年不再支持)及来自2012年的WebKit分支,并明确警告不要将其用于不受信任的HTML。 CVE-2022-35583没有修复版本,因此任何使用Rotativa的应用程序都存在风险,除非HTML输入已经在上游完全清理。
攻击如何工作
<!-- Attacker submits this content via your MVC model -->
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin" />
影响:
- 访问 AWS/Azure/GCP 云元数据端点
- 窃取内部 API 数据和凭证
- 端口扫描内部网络
- 渗出敏感配置
技术危机
Rotativa 对 wkhtmltopdf 进行了包装,后者使用的是:
- Qt WebKit 4.8(2012 年起)
- 不支持 Flexbox
- 不支持 CSS 网格
- JavaScript 执行中断
- 不支持 ES6+
###Rotativa与IronPDF对比
| 特征 | Rotativa | IronPDF |
|---|---|---|
| 项目兼容性 | 仅限 ASP.NET MVC | 任何 .NET 项目类型(MVC、Razor Pages、Blazor 等) |
| 维护 | 放弃 | 积极维护 |
| 安全性 | 由于 wkhtmltopdf 依赖关系而存在漏洞 (CVE-2022-35583) | 定期更新和安全补丁 |
| HTML 渲染 | 过时的 WebKit | 现代 Chromium |
| CSS3 | 部分 | 支持 |
| Flexbox/网格 | 不支持 | 支持 |
| JavaScript | 不可靠 | 完整的 ES6+ |
| Razor页面 | 不支持 | 支持 |
| Blazor | 不支持 | 支持 |
| PDF 操作 | 不可用 | 完整 |
| 数字签名 | 不可用 | 完整 |
| PDF/A合规性 | 不可用 | 完整 |
| 同步/等待 | 仅同步 | 完全异步 |
| 开放源代码 | 是,MIT 许可 | 否,商业许可 |
对于目标是当前.NET版本的团队,IronPDF提供了Rotativa无法提供的现代Chromium渲染和跨平台支持。
开始之前
前提条件
- .NET 环境: .NET Framework 4.6.2+ 或 .NET Core 3.1+ / .NET 5/6/7/8/9+
- NuGet 访问权限:能够安装 NuGet 包
- IronPDF 许可证:请从ironpdf.com获取您的许可证密钥。
NuGet 软件包变更
原始Rotativa包针对经典ASP.NET MVC 5(最后的发布版本v1.7.3,2017-09-29)。 社区分叉Rotativa.AspNetCore(webgio)于2024-11-06达到了v1.4.0,并支持.NET Core 3.1到.NET 8 —— 但两个包仍然附带wkhtmltopdf 0.12.6二进制文件(最后的上游发布2020年6月)。
# Remove Rotativa
dotnet remove package Rotativa
dotnet remove package Rotativa.AspNetCore
# Install IronPDF
dotnet add package IronPdf
删除 wkhtmltopdf 二进制文件
从您的项目中删除这些文件:
wkhtmltopdf.exewkhtmltox.dll- 任何
Rotativa/文件夹
这些就是CVE-2022-35583的源头。IronPDF 不需要本地二进制文件。
许可配置
// Add in Program.cs or Startup.cs
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";' Add in Program.vb or Startup.vb
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"' Add in Program.vb or Startup.vb
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"完整的 API 参考
命名空间变更
// Before: Rotativa
using Rotativa;
using Rotativa.Options;
using Rotativa.AspNetCore;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;// Before: Rotativa
using Rotativa;
using Rotativa.Options;
using Rotativa.AspNetCore;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;' Before: Rotativa
Imports Rotativa
Imports Rotativa.Options
Imports Rotativa.AspNetCore
' After: IronPDF
Imports IronPdf
Imports IronPdf.Rendering' Before: Rotativa
Imports Rotativa
Imports Rotativa.Options
Imports Rotativa.AspNetCore
' After: IronPDF
Imports IronPdf
Imports IronPdf.Rendering核心类映射
| Rotativa 类 | IronPDF 同等产品 |
|---|---|
ViewAsPdf | ChromePdfRenderer |
ActionAsPdf | ChromePdfRenderer.RenderUrlAsPdf() |
UrlAsPdf | ChromePdfRenderer.RenderUrlAsPdf() |
Orientation枚举 | PdfPaperOrientation枚举 |
Size枚举 | PdfPaperSize枚举 |
页面占位符转换
| Rotativa 占位符 | IronPDF 占位符 |
|---|---|
[page] | {page} |
[topage] | {total-pages} |
[date] | {date} |
[time] | {time} |
[title] | {html-title} |
[sitepage] | {url} |
代码迁移示例
示例 1:HTML 到 PDF 的转换
之前 (Rotativa):
// NuGet: Install-Package Rotativa.AspNetCore
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class PdfController : Controller
{
public async Task<IActionResult> GeneratePdf()
{
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
//Rotativarequires returning a ViewAsPdf result from MVC controller
return new ViewAsPdf()
{
ViewName = "PdfView",
PageSize = Rotativa.AspNetCore.Options.Size.A4
};
}
}
}// NuGet: Install-Package Rotativa.AspNetCore
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class PdfController : Controller
{
public async Task<IActionResult> GeneratePdf()
{
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
//Rotativarequires returning a ViewAsPdf result from MVC controller
return new ViewAsPdf()
{
ViewName = "PdfView",
PageSize = Rotativa.AspNetCore.Options.Size.A4
};
}
}
}Imports Microsoft.AspNetCore.Mvc
Imports Rotativa.AspNetCore
Imports System.Threading.Tasks
Namespace RotativaExample
Public Class PdfController
Inherits Controller
Public Async Function GeneratePdf() As Task(Of IActionResult)
Dim htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>"
'Rotavita requires returning a ViewAsPdf result from MVC controller
Return New ViewAsPdf() With {
.ViewName = "PdfView",
.PageSize = Rotativa.AspNetCore.Options.Size.A4
}
End Function
End Class
End NamespaceImports Microsoft.AspNetCore.Mvc
Imports Rotativa.AspNetCore
Imports System.Threading.Tasks
Namespace RotativaExample
Public Class PdfController
Inherits Controller
Public Async Function GeneratePdf() As Task(Of IActionResult)
Dim htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>"
'Rotavita requires returning a ViewAsPdf result from MVC controller
Return New ViewAsPdf() With {
.ViewName = "PdfView",
.PageSize = Rotativa.AspNetCore.Options.Size.A4
}
End Function
End Class
End NamespaceAfter (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully!");
}
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var htmlContent = "<h1>Hello World</h1><p>This is a PDF document.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully!");
}
}
}' NuGet: Install-Package IronPdf
Imports IronPdf
Imports System
Module IronPdfExample
Sub Main()
Dim renderer As New ChromePdfRenderer()
Dim htmlContent As String = "<h1>Hello World</h1><p>This is a PDF document.</p>"
Dim pdf = renderer.RenderHtmlAsPdf(htmlContent)
pdf.SaveAs("output.pdf")
Console.WriteLine("PDF generated successfully!")
End Sub
End Module' NuGet: Install-Package IronPdf
Imports IronPdf
Imports System
Module IronPdfExample
Sub Main()
Dim renderer As New ChromePdfRenderer()
Dim htmlContent As String = "<h1>Hello World</h1><p>This is a PDF document.</p>"
Dim pdf = renderer.RenderHtmlAsPdf(htmlContent)
pdf.SaveAs("output.pdf")
Console.WriteLine("PDF generated successfully!")
End Sub
End Module本例展示了基本的架构差异。 Rotativa需要从MVC控制器操作返回ViewAsPdf结果,将您绑定到ASP.NET MVC框架。 该模式仅适用于 MVC 请求管道,并需要 Razor 视图来呈现。
IronPDF 适用于任何地方:控制台应用程序、Web API、Blazor、Razor Pages 或任何 .NET 项目类型。 您调用RenderHtmlAsPdf()与一个HTML字符串并保存结果。 无需 MVC 控制器,不依赖视图。 请参阅 HTML 转 PDF 文档,了解全面的示例。
示例 2:URL 到 PDF 的转换
之前 (Rotativa):
// NuGet: Install-Package Rotativa.AspNetCore
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class UrlPdfController : Controller
{
public async Task<IActionResult> ConvertUrlToPdf()
{
//Rotativaworks within MVC framework and returns ActionResult
return new UrlAsPdf("https://www.example.com")
{
FileName = "webpage.pdf",
PageSize = Rotativa.AspNetCore.Options.Size.A4,
PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait
};
}
}
}// NuGet: Install-Package Rotativa.AspNetCore
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using System.Threading.Tasks;
namespace RotativaExample
{
public class UrlPdfController : Controller
{
public async Task<IActionResult> ConvertUrlToPdf()
{
//Rotativaworks within MVC framework and returns ActionResult
return new UrlAsPdf("https://www.example.com")
{
FileName = "webpage.pdf",
PageSize = Rotativa.AspNetCore.Options.Size.A4,
PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait
};
}
}
}' NuGet: Install-Package Rotativa.AspNetCore
Imports Microsoft.AspNetCore.Mvc
Imports Rotativa.AspNetCore
Imports System.Threading.Tasks
Namespace RotativaExample
Public Class UrlPdfController
Inherits Controller
Public Async Function ConvertUrlToPdf() As Task(Of IActionResult)
'Rotativa works within MVC framework and returns ActionResult
Return New UrlAsPdf("https://www.example.com") With {
.FileName = "webpage.pdf",
.PageSize = Rotativa.AspNetCore.Options.Size.A4,
.PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait
}
End Function
End Class
End Namespace' NuGet: Install-Package Rotativa.AspNetCore
Imports Microsoft.AspNetCore.Mvc
Imports Rotativa.AspNetCore
Imports System.Threading.Tasks
Namespace RotativaExample
Public Class UrlPdfController
Inherits Controller
Public Async Function ConvertUrlToPdf() As Task(Of IActionResult)
'Rotativa works within MVC framework and returns ActionResult
Return New UrlAsPdf("https://www.example.com") With {
.FileName = "webpage.pdf",
.PageSize = Rotativa.AspNetCore.Options.Size.A4,
.PageOrientation = Rotativa.AspNetCore.Options.Orientation.Portrait
}
End Function
End Class
End NamespaceAfter (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("URL converted to PDF successfully!");
}
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("URL converted to PDF successfully!");
}
}
}' NuGet: Install-Package IronPdf
Imports IronPdf
Imports System
Module IronPdfExample
Sub Main()
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderUrlAsPdf("https://www.example.com")
pdf.SaveAs("webpage.pdf")
Console.WriteLine("URL converted to PDF successfully!")
End Sub
End Module' NuGet: Install-Package IronPdf
Imports IronPdf
Imports System
Module IronPdfExample
Sub Main()
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderUrlAsPdf("https://www.example.com")
pdf.SaveAs("webpage.pdf")
Console.WriteLine("URL converted to PDF successfully!")
End Sub
End ModuleRotativa的UrlAsPdf类需要从 MVC 控制器返回一个ActionResult。 IronPDF的RenderUrlAsPdf()方法可以从任何上下文调用,并直接返回一个PdfDocument对象。 URL 渲染使用的是现代 Chromium 引擎,而不是 wkhtmltopdf 脆弱过时的 WebKit 引擎。请在我们的教程中了解更多信息。
示例 3:带页码的页眉和页脚
之前 (Rotativa):
// NuGet: Install-Package Rotativa.AspNetCore
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using Rotativa.AspNetCore.Options;
using System.Threading.Tasks;
namespace RotativaExample
{
public class HeaderFooterController : Controller
{
public async Task<IActionResult> GeneratePdfWithHeaderFooter()
{
return new ViewAsPdf("Report")
{
PageSize = Size.A4,
// Margins(top, right, bottom, left)
PageMargins = new Margins(20, 10, 20, 10),
CustomSwitches = "--header-center \"Page Header\" --footer-center \"Page [page] of [topage]\""
};
}
}
}// NuGet: Install-Package Rotativa.AspNetCore
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore;
using Rotativa.AspNetCore.Options;
using System.Threading.Tasks;
namespace RotativaExample
{
public class HeaderFooterController : Controller
{
public async Task<IActionResult> GeneratePdfWithHeaderFooter()
{
return new ViewAsPdf("Report")
{
PageSize = Size.A4,
// Margins(top, right, bottom, left)
PageMargins = new Margins(20, 10, 20, 10),
CustomSwitches = "--header-center \"Page Header\" --footer-center \"Page [page] of [topage]\""
};
}
}
}Imports Microsoft.AspNetCore.Mvc
Imports Rotativa.AspNetCore
Imports Rotativa.AspNetCore.Options
Imports System.Threading.Tasks
Namespace RotativaExample
Public Class HeaderFooterController
Inherits Controller
Public Async Function GeneratePdfWithHeaderFooter() As Task(Of IActionResult)
Return New ViewAsPdf("Report") With {
.PageSize = Size.A4,
' Margins(top, right, bottom, left)
.PageMargins = New Margins(20, 10, 20, 10),
.CustomSwitches = "--header-center ""Page Header"" --footer-center ""Page [page] of [topage]"""
}
End Function
End Class
End NamespaceImports Microsoft.AspNetCore.Mvc
Imports Rotativa.AspNetCore
Imports Rotativa.AspNetCore.Options
Imports System.Threading.Tasks
Namespace RotativaExample
Public Class HeaderFooterController
Inherits Controller
Public Async Function GeneratePdfWithHeaderFooter() As Task(Of IActionResult)
Return New ViewAsPdf("Report") With {
.PageSize = Size.A4,
' Margins(top, right, bottom, left)
.PageMargins = New Margins(20, 10, 20, 10),
.CustomSwitches = "--header-center ""Page Header"" --footer-center ""Page [page] of [topage]"""
}
End Function
End Class
End NamespaceAfter (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
{
CenterText = "Page Header",
DrawDividerLine = true
};
renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
{
CenterText = "Page {page} of {total-pages}",
DrawDividerLine = true
};
var htmlContent = "<h1>Report Title</h1><p>Report content goes here.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("report.pdf");
Console.WriteLine("PDF with headers and footers created successfully!");
}
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
namespace IronPdfExample
{
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TextHeader = new TextHeaderFooter()
{
CenterText = "Page Header",
DrawDividerLine = true
};
renderer.RenderingOptions.TextFooter = new TextHeaderFooter()
{
CenterText = "Page {page} of {total-pages}",
DrawDividerLine = true
};
var htmlContent = "<h1>Report Title</h1><p>Report content goes here.</p>";
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("report.pdf");
Console.WriteLine("PDF with headers and footers created successfully!");
}
}
}' NuGet: Install-Package IronPdf
Imports IronPdf
Imports IronPdf.Rendering
Imports System
Module IronPdfExample
Sub Main(args As String())
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.TextHeader = New TextHeaderFooter() With {
.CenterText = "Page Header",
.DrawDividerLine = True
}
renderer.RenderingOptions.TextFooter = New TextHeaderFooter() With {
.CenterText = "Page {page} of {total-pages}",
.DrawDividerLine = True
}
Dim htmlContent As String = "<h1>Report Title</h1><p>Report content goes here.</p>"
Dim pdf = renderer.RenderHtmlAsPdf(htmlContent)
pdf.SaveAs("report.pdf")
Console.WriteLine("PDF with headers and footers created successfully!")
End Sub
End Module' NuGet: Install-Package IronPdf
Imports IronPdf
Imports IronPdf.Rendering
Imports System
Module IronPdfExample
Sub Main(args As String())
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.TextHeader = New TextHeaderFooter() With {
.CenterText = "Page Header",
.DrawDividerLine = True
}
renderer.RenderingOptions.TextFooter = New TextHeaderFooter() With {
.CenterText = "Page {page} of {total-pages}",
.DrawDividerLine = True
}
Dim htmlContent As String = "<h1>Report Title</h1><p>Report content goes here.</p>"
Dim pdf = renderer.RenderHtmlAsPdf(htmlContent)
pdf.SaveAs("report.pdf")
Console.WriteLine("PDF with headers and footers created successfully!")
End Sub
End ModuleRotativa使用CustomSwitches向 wkhtmltopdf 传递命令行参数,包括使用[page]和[topage]等占位符的页眉和页脚配置。 这种基于字符串的方法容易出错,而且很难在编译时进行验证。
IronPDF使用强类型的TextHeaderFooter对象,带有CenterText和DrawDividerLine这样的属性。 占位符语法从[page]更改为{page},从[topage]更改为{total-pages}。 类型化属性提供了智能提示、编译时检查功能,而且不会出现错别字。
仅 MVC 架构问题
Rotativa 专为 ASP.NET MVC 5 及更早版本设计:
// ❌Rotativa- Only works with classic MVC pattern
public class InvoiceController : Controller
{
public ActionResult InvoicePdf(int id)
{
var model = GetInvoice(id);
return new ViewAsPdf("Invoice", model); // Tied to MVC Views
}
}
// Problems:
// - No Razor Pages support
// - No Blazor support
// - No minimal APIs support
// - No ASP.NET Core native integration// ❌Rotativa- Only works with classic MVC pattern
public class InvoiceController : Controller
{
public ActionResult InvoicePdf(int id)
{
var model = GetInvoice(id);
return new ViewAsPdf("Invoice", model); // Tied to MVC Views
}
}
// Problems:
// - No Razor Pages support
// - No Blazor support
// - No minimal APIs support
// - No ASP.NET Core native integrationImports System.Web.Mvc
Public Class InvoiceController
Inherits Controller
Public Function InvoicePdf(id As Integer) As ActionResult
Dim model = GetInvoice(id)
Return New ViewAsPdf("Invoice", model) ' Tied to MVC Views
End Function
End Class
' Problems:
' - No Razor Pages support
' - No Blazor support
' - No minimal APIs support
' - No ASP.NET Core native integrationImports System.Web.Mvc
Public Class InvoiceController
Inherits Controller
Public Function InvoicePdf(id As Integer) As ActionResult
Dim model = GetInvoice(id)
Return New ViewAsPdf("Invoice", model) ' Tied to MVC Views
End Function
End Class
' Problems:
' - No Razor Pages support
' - No Blazor support
' - No minimal APIs support
' - No ASP.NET Core native integrationIronPDF 将视图渲染与 PDF 生成分离开来,这实际上更加灵活--您可以渲染任何 HTML,而不仅仅是 MVC 视图。
同步模式迁移
Rotativa 阻断了线程;IronPDF支持完全Async/Await:
// ❌Rotativa- Blocks the thread
public ActionResult GeneratePdf()
{
return new ViewAsPdf("Report");
// This blocks the request thread until PDF is complete
// Poor scalability under load
}
// ✅IronPDF-完全异步support
public async Task<IActionResult> GeneratePdf()
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return File(pdf.BinaryData, "application/pdf");
// Non-blocking, better scalability
}// ❌Rotativa- Blocks the thread
public ActionResult GeneratePdf()
{
return new ViewAsPdf("Report");
// This blocks the request thread until PDF is complete
// Poor scalability under load
}
// ✅IronPDF-完全异步support
public async Task<IActionResult> GeneratePdf()
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return File(pdf.BinaryData, "application/pdf");
// Non-blocking, better scalability
}' ❌Rotativa- Blocks the thread
Public Function GeneratePdf() As ActionResult
Return New ViewAsPdf("Report")
' This blocks the request thread until PDF is complete
' Poor scalability under load
End Function
' ✅IronPDF-完全异步support
Public Async Function GeneratePdf() As Task(Of IActionResult)
Dim renderer As New ChromePdfRenderer()
Dim pdf = Await renderer.RenderHtmlAsPdfAsync(html)
Return File(pdf.BinaryData, "application/pdf")
' Non-blocking, better scalability
End Function' ❌Rotativa- Blocks the thread
Public Function GeneratePdf() As ActionResult
Return New ViewAsPdf("Report")
' This blocks the request thread until PDF is complete
' Poor scalability under load
End Function
' ✅IronPDF-完全异步support
Public Async Function GeneratePdf() As Task(Of IActionResult)
Dim renderer As New ChromePdfRenderer()
Dim pdf = Await renderer.RenderHtmlAsPdfAsync(html)
Return File(pdf.BinaryData, "application/pdf")
' Non-blocking, better scalability
End Function迁移后的新功能
迁移到IronPDF后,您将获得Rotativa无法提供的功能:
PDF 合并
var merged = PdfDocument.Merge(pdf1, pdf2, pdf3);
merged.SaveAs("complete.pdf");var merged = PdfDocument.Merge(pdf1, pdf2, pdf3);
merged.SaveAs("complete.pdf");Dim merged = PdfDocument.Merge(pdf1, pdf2, pdf3)
merged.SaveAs("complete.pdf")Dim merged = PdfDocument.Merge(pdf1, pdf2, pdf3)
merged.SaveAs("complete.pdf")数字签名
var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);Dim signature = New PdfSignature("certificate.pfx", "password")
pdf.Sign(signature)Dim signature = New PdfSignature("certificate.pfx", "password")
pdf.Sign(signature)密码保护
pdf.SecuritySettings.UserPassword = "secret";pdf.SecuritySettings.UserPassword = "secret";pdf.SecuritySettings.UserPassword = "secret"pdf.SecuritySettings.UserPassword = "secret"水印
pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");IRON VB CONVERTER ERROR developers@ironsoftware.comIRON VB CONVERTER ERROR developers@ironsoftware.comPDF/A 存档合规性
pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b)pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b)现代 CSS 支持
// This now works (broke in Rotativa)
var html = @"
<div style='display: flex; justify-content: space-between;'>
<div>Left</div>
<div>Right</div>
</div>
<div style='display: grid; grid-template-columns: 1fr 1fr 1fr;'>
<div>Col 1</div><div>Col 2</div><div>Col 3</div>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html); // Works!// This now works (broke in Rotativa)
var html = @"
<div style='display: flex; justify-content: space-between;'>
<div>Left</div>
<div>Right</div>
</div>
<div style='display: grid; grid-template-columns: 1fr 1fr 1fr;'>
<div>Col 1</div><div>Col 2</div><div>Col 3</div>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html); // Works!' This now works (broke in Rotativa)
Dim html As String = "
<div style='display: flex; justify-content: space-between;'>
<div>Left</div>
<div>Right</div>
</div>
<div style='display: grid; grid-template-columns: 1fr 1fr 1fr;'>
<div>Col 1</div><div>Col 2</div><div>Col 3</div>
</div>"
Dim pdf = renderer.RenderHtmlAsPdf(html) ' Works!' This now works (broke in Rotativa)
Dim html As String = "
<div style='display: flex; justify-content: space-between;'>
<div>Left</div>
<div>Right</div>
</div>
<div style='display: grid; grid-template-columns: 1fr 1fr 1fr;'>
<div>Col 1</div><div>Col 2</div><div>Col 3</div>
</div>"
Dim pdf = renderer.RenderHtmlAsPdf(html) ' Works!迁移清单
迁移前
- 识别代码库中所有Rotativa的使用情况
- 文档 CustomSwitches 用于转换为 RenderingOptions
- 注意页眉/页脚占位符语法用于转换(
[page]→{page}) - 从ironpdf.com获取IronPDF许可证密钥
软件包变更
- 移除
Rotativa和Rotativa.AspNetCoreNuGet包 - 删除wkhtmltopdf二进制文件(
wkhtmltopdf.exe、wkhtmltox.dll) - 安装
IronPdfNuGet包
代码更改
- 更新命名空间导入(
using Rotativa;→using IronPdf;) - 将
ViewAsPdf替换为ChromePdfRenderer+RenderHtmlAsPdf() - 将
UrlAsPdf替换为RenderUrlAsPdf() - 将
CustomSwitches转换为RenderingOptions属性 - 更新占位符语法(
[page]→{page},[topage]→{total-pages}) - 将
PageMargins替换为单独的MarginTop/MarginBottom/MarginLeft/MarginRight - 在适当情况下改为异步模式
- 在应用程序启动时添加许可证初始化
后迁移
- 验证所有 PDF 生成功能是否正常
- 比较 PDF 输出质量(Chromium 的渲染更准确)
- 验证 CSS 渲染改进(Flexbox/Grid 现在可以正常工作)
- 测试 JavaScript 执行(现在使用 Chromium 内核已稳定运行)
- 验证安全扫描通过(不再有CVE-2022-35583标志)
- 更新 Docker 配置以移除 wkhtmltopdf 安装
Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。
相关文章


