Gotenberg 与 IronPDF:技术比较指南
当 .NET 开发人员评估 PDF 生成解决方案时,Gotenberg 脱颖而出,它是一个基于 Docker 的微服务,通过 REST API 调用将 HTML 转换为 PDF。 虽然高登堡能够适应各种不同的架构,但它也带来了显著的基础设施开销——Docker 容器、网络延迟和操作复杂性。IronPDF提供了一种替代方案:一个进程内 NuGet 包,提供相同的基于 Chromium 的渲染,而无需容器、网络调用或基础架构管理。
本次比较从技术相关维度对两种解决方案进行了考察,以帮助专业开发人员和架构师根据其 .NET PDF 需求做出明智的决策。
了解哥登堡
Gotenberg 是一种基于 Docker 的 PDF 生成微服务架构。 它作为一个单独的容器运行,可提供 REST API 端点,用于将 HTML、URL 和其他格式转换为 PDF。 每个 PDF 操作都需要对高登堡服务进行 HTTP 调用。
Gotenberg 使用POST /forms/chromium/convert/html等端点实现 HTML 到 PDF 的转换,使用POST /forms/chromium/convert/url实现 URL 到 PDF 的转换。 配置通过多art/form-data 传递,参数基于字符串,如 paperWidth、paperHeight、marginTop 和 marginBottom(单位:英寸)。 该服务需要 Docker 部署、容器编排(Kubernetes/Docker Compose)和网络基础设施。
架构要求
- Docker 容器部署和管理
- 每个 PDF 请求的网络通信(10-100 毫秒以上的延迟)
- 容器冷启动处理(首次请求 2-5 秒)
- 健康检查端点和服务监控
- 为每个请求提供多格式/表单数据结构
了解IronPDF
IronPDF 是一个本地 .NET 库,作为一个 NuGet 包在进程中运行。 它提供基于 Chromium 的 HTML 渲染,无需外部服务、网络调用或容器基础架构。
IronPDF 使用 ChromePdfRenderer 作为其主要的渲染类,并使用 RenderHtmlAsPdf() 和 RenderUrlAsPdf() 等方法。 配置使用 RenderingOptions 上的类型化 C# 属性,包括 PaperSize, MarginTop, MarginBottom (单位:毫米)。 文档使用 SaveAs() 保存,或以 BinaryData 的形式访问。
该库只需要
- NuGet 软件包安装(`dotnet 添加软件包 IronPdf</code)
- 许可证密钥配置
- 标准 .NET 项目设置
架构和基础架构比较
这些解决方案的根本区别在于其部署和运行时架构。
| 因素 | 高登堡 | IronPDF |
|---|---|---|
| 部署 | Docker 容器 + 协调 | 单个 NuGet 软件包 |
| 架构 | 微服务(REST API) | 在建库 |
| 每次请求的延迟时间 | 10-100ms+(网络往返) | < 1ms 开销 |
| 冷启动 | 2-5 秒(容器启动) | 1-2 秒(仅限首次呈现) |
| 基础设施 | Docker、Kubernetes、负载平衡器 | 无要求 |
| 网络依赖性 | 要求 | 无 |
| 失败模式 | 网络、容器、服务故障 | .NET 标准例外情况 |
| API 风格 | REST 多art/表单数据 | 本地 C# 方法调用 |
| 扩展 | 横向(更多容器) | 纵向(处理中) |
| 调试 | 分布式跟踪 | 标准调试器 |
| 内存管理 | 独立容器(512MB-2GB) | 共享应用程序内存 |
| 版本控制 | 容器图像标签 | NuGet 软件包版本 |
| 健康检查 | 所需的 HTTP 端点 | 不需要(处理中) |
| CI/CD复杂性 | 容器构建、注册表推送 | 标准 .NET 构建 |
Gotenberg 基于 Docker 的方法需要容器部署、健康监控和网络基础设施管理。IronPDF通过在进程中运行,完全消除了这一基础架构层。
代码比较:常见的 PDF 操作
基本 HTML 到 PDF 的转换
最基本的操作清楚地展示了架构上的差异。
戈登堡:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergExample
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Hello from Gotenberg</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("output.pdf", pdfBytes);
Console.WriteLine("PDF generated successfully");
}
}using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergExample
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Hello from Gotenberg</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("output.pdf", pdfBytes);
Console.WriteLine("PDF generated successfully");
}
}Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Module GotenbergExample
Async Function Main() As Task
Dim gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
Dim html = "<html><body><h1>Hello from Gotenberg</h1></body></html>"
content.Add(New StringContent(html), "files", "index.html")
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("output.pdf", pdfBytes)
Console.WriteLine("PDF generated successfully")
End Using
End Using
End Function
End ModuleIronPDF:
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfExample
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var html = "<html><body><h1>Hello from IronPDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully");
}
}// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfExample
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var html = "<html><body><h1>Hello from IronPDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
Console.WriteLine("PDF generated successfully");
}
}Imports System
Imports IronPdf
Class IronPdfExample
Shared Sub Main()
Dim renderer = New ChromePdfRenderer()
Dim html = "<html><body><h1>Hello from IronPDF</h1></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("output.pdf")
Console.WriteLine("PDF generated successfully")
End Sub
End ClassGotenberg 要求创建一个 HttpClient ,构建 MultipartFormDataContent ,将 HTML 添加为具有特定文件名(index.html)的文件附件,向高登堡服务端点进行异步 HTTP POST,读取响应字节并写入磁盘。 每个请求都要通过网络,并伴有延迟和故障模式。
IronPDF 创建一个 ChromePdfRenderer 文件,使用 HTML 字符串调用 RenderHtmlAsPdf() 并使用 SaveAs() 保存。 操作是同步的、进程中的,并使用类型化方法而不是基于字符串的表单数据。
有关高级 HTML 渲染选项,请浏览 HTML 到 PDF 转换指南。
URL到PDF转换
将实时网页转换为 PDF 显示了类似的架构模式。
戈登堡:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergUrlToPdf
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/url";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
content.Add(new StringContent("https://example.com"), "url");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("webpage.pdf", pdfBytes);
Console.WriteLine("PDF from URL generated successfully");
}
}using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergUrlToPdf
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/url";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
content.Add(new StringContent("https://example.com"), "url");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("webpage.pdf", pdfBytes);
Console.WriteLine("PDF from URL generated successfully");
}
}Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Module GotenbergUrlToPdf
Async Function Main() As Task
Dim gotenbergUrl As String = "http://localhost:3000/forms/chromium/convert/url"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
content.Add(New StringContent("https://example.com"), "url")
Dim response As HttpResponseMessage = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes As Byte() = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("webpage.pdf", pdfBytes)
Console.WriteLine("PDF from URL generated successfully")
End Using
End Using
End Function
End ModuleIronPDF:
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfUrlToPdf
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("PDF from URL generated successfully");
}
}// NuGet: Install-Package IronPdf
using System;
using IronPdf;
class IronPdfUrlToPdf
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("webpage.pdf");
Console.WriteLine("PDF from URL generated successfully");
}
}Imports System
Imports IronPdf
Class IronPdfUrlToPdf
Shared Sub Main()
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderUrlAsPdf("https://example.com")
pdf.SaveAs("webpage.pdf")
Console.WriteLine("PDF from URL generated successfully")
End Sub
End ClassGotenberg 使用 /forms/chromium/convert/url 端点,并将 URL 作为表单数据传递。IronPDF使用 URL 字符串直接调用 RenderUrlAsPdf() - 一个方法调用取代了 HTTP 基础架构。
自定义页面大小和页边距
配置处理揭示了 API 的设计差异。
戈登堡:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergCustomSize
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
content.Add(new StringContent("8.5"), "paperWidth");
content.Add(new StringContent("11"), "paperHeight");
content.Add(new StringContent("0.5"), "marginTop");
content.Add(new StringContent("0.5"), "marginBottom");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("custom-size.pdf", pdfBytes);
Console.WriteLine("Custom size PDF generated successfully");
}
}using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
class GotenbergCustomSize
{
static async Task Main()
{
var gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html";
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
content.Add(new StringContent(html), "files", "index.html");
content.Add(new StringContent("8.5"), "paperWidth");
content.Add(new StringContent("11"), "paperHeight");
content.Add(new StringContent("0.5"), "marginTop");
content.Add(new StringContent("0.5"), "marginBottom");
var response = await client.PostAsync(gotenbergUrl, content);
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("custom-size.pdf", pdfBytes);
Console.WriteLine("Custom size PDF generated successfully");
}
}Imports System
Imports System.Net.Http
Imports System.Threading.Tasks
Imports System.IO
Class GotenbergCustomSize
Shared Async Function Main() As Task
Dim gotenbergUrl = "http://localhost:3000/forms/chromium/convert/html"
Using client As New HttpClient()
Using content As New MultipartFormDataContent()
Dim html = "<html><body><h1>Custom Size PDF</h1></body></html>"
content.Add(New StringContent(html), "files", "index.html")
content.Add(New StringContent("8.5"), "paperWidth")
content.Add(New StringContent("11"), "paperHeight")
content.Add(New StringContent("0.5"), "marginTop")
content.Add(New StringContent("0.5"), "marginBottom")
Dim response = Await client.PostAsync(gotenbergUrl, content)
Dim pdfBytes = Await response.Content.ReadAsByteArrayAsync()
Await File.WriteAllBytesAsync("custom-size.pdf", pdfBytes)
Console.WriteLine("Custom size PDF generated successfully")
End Using
End Using
End Function
End ClassIronPDF:
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
using IronPdf.Rendering;
class IronPdfCustomSize
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 50;
renderer.RenderingOptions.MarginBottom = 50;
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("custom-size.pdf");
Console.WriteLine("Custom size PDF generated successfully");
}
}// NuGet: Install-Package IronPdf
using System;
using IronPdf;
using IronPdf.Rendering;
class IronPdfCustomSize
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 50;
renderer.RenderingOptions.MarginBottom = 50;
var html = "<html><body><h1>Custom Size PDF</h1></body></html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("custom-size.pdf");
Console.WriteLine("Custom size PDF generated successfully");
}
}Imports System
Imports IronPdf
Imports IronPdf.Rendering
Module IronPdfCustomSize
Sub Main()
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter
renderer.RenderingOptions.MarginTop = 50
renderer.RenderingOptions.MarginBottom = 50
Dim html As String = "<html><body><h1>Custom Size PDF</h1></body></html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("custom-size.pdf")
Console.WriteLine("Custom size PDF generated successfully")
End Sub
End ModuleGotenberg 使用基于字符串的参数("8.5", "11", "0.5")添加到多部分表单数据中。 纸张尺寸以英寸为单位。 每个参数都是一个单独的 Add() 调用,没有类型检查或 IntelliSense 支持。
IronPDF 在 RenderingOptions 上使用类型化属性。 PaperSize 接受一个枚举(PdfPaperSize.Letter),页边距是以毫米为单位的数值。 类型化 API 提供编译时检查和 IDE 支持。
在 IronPDF 教程中了解有关渲染配置的更多信息。
API 映射参考
对于正在评估高登堡迁移或比较功能的开发人员,本映射说明了等价操作:
端点到方法映射
| 哥登堡路线 | IronPdf 同等产品 |
|---|---|
POST /forms/chromium/convert/html | |
POST /forms/chromium/convert/url | ChromePdfRenderer.RenderUrlAsPdf() |
| `POST /forms/chromium/convert/markdown`` | 先将 Markdown 渲染为 HTML |
POST /forms/pdfengines/merge``|PdfDocument.Merge()` | |
POST /forms/pdfengines/metadata/read | pdf.MetaData |
POST /forms/pdfengines/metadata/write``|pdf.MetaData.Author = "..."` | |
获取 /health | 不适用 |
表单参数到渲染选项映射
| 高登贝格参数 | IronPdf 属性 | 转换注意事项 |
|---|---|---|
纸张宽度</code>(英寸)|<code>RenderingOptions.SetCustomPaperSizeInInches() | 自定义使用方法 | |
纸张高度</code>(英寸)|<code>RenderingOptions.SetCustomPaperSizeInInches() | 自定义使用方法 | |
marginTop</code>(英寸)|<code>RenderingOptions.MarginTop | 毫米数乘以 25.4 | |
marginBottom</code>(英寸)|<code>RenderingOptions.MarginBottom | 毫米数乘以 25.4 | |
marginLeft</code>(英寸)|<code>RenderingOptions.MarginLeft | 毫米数乘以 25.4 | |
marginRight</code>(英寸)|<code>RenderingOptions.MarginRight | 毫米数乘以 25.4 | |
打印背景 | RenderingOptions.PrintHtmlBackgrounds | 布尔 |
景观 | RenderingOptions.PaperOrientation | Landscape 枚举 |
scale | RenderingOptions.Zoom | 百分比(100 = 1.0) |
waitDelay | RenderingOptions.RenderDelay | 转换为毫秒 |
emulatedMediaType | RenderingOptions.CssMediaType | 屏幕</code>或 <code>打印 |
注意单位换算:Gotenberg 使用英寸表示页边距(例如,"0.5" = 0.5 英寸 = 12.7 毫米),而IronPDF使用毫米。
基础架构比较
哥登堡 Docker Compose
Gotenberg 需要容器基础设施:
#高登堡requires container management
version: '3.8'
services:
app:
depends_on:
- gotenberg
environment:
- GOTENBERG_URL=http://gotenberg:3000
gotenberg:
image: gotenberg/gotenberg:8
ports:
- "3000:3000"
deploy:
resources:
limits:
memory: 2G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s#高登堡requires container management
version: '3.8'
services:
app:
depends_on:
- gotenberg
environment:
- GOTENBERG_URL=http://gotenberg:3000
gotenberg:
image: gotenberg/gotenberg:8
ports:
- "3000:3000"
deploy:
resources:
limits:
memory: 2G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30sIronPDF配置
IronPdf 无需额外服务:
#IronPDF- No additional services needed
version: '3.8'
services:
app:
environment:
- IRONPDF_LICENSE_KEY=${IRONPDF_LICENSE_KEY}
# No高登堡service. No health checks. No resource limits.#IronPDF- No additional services needed
version: '3.8'
services:
app:
environment:
- IRONPDF_LICENSE_KEY=${IRONPDF_LICENSE_KEY}
# No高登堡service. No health checks. No resource limits.基础设施差异很大:Gotenberg 需要容器部署、健康监控、资源分配和服务依赖性。 IronPdf 与应用程序一起在进程中运行。
性能特点
| 手术 | 哥登堡(温暖的集装箱) | 高登堡(冷启动) | IronPdf (初渲染) | IronPdf (后续) |
|---|---|---|---|---|
| 简单的 HTML | 150-300 毫秒 | 2-5 秒 | 1-2 秒 | 50-150ms |
| 复杂的 HTML | 500-1500ms | 3-7 秒 | 1.5-3 秒 | 200-800ms |
| URL 渲染 | 1-5 秒 | 3-10 秒 | 1-5 秒 | 500ms-3s |
| PDF 合并 | 200-500 毫秒 | 2-5 秒 | 100-300 毫秒 | 100-300 毫秒 |
Gotenberg 的网络往返每次请求会增加 10-100ms+ 的时间。容器冷启动会增加 2-5 秒。IronPDF的首次渲染会产生 Chromium 初始化(1-2 秒),但后续渲染的开销极小。
团队何时考虑从高登堡迁移到 IronPDF?
开发团队评估从高登堡向IronPDF过渡的原因有以下几点:
基础设施开销:高登堡需要 Docker、容器编排(Kubernetes/Docker Compose)、服务发现和负载均衡。 寻求更简单部署的团队发现IronPDF的 NuGet 方法消除了这些基础架构方面的顾虑。
网络延迟:高登堡的每次 PDF 操作都需要向单独的服务发起 HTTP 请求,每次请求会增加 10-100 毫秒甚至更多的延迟。对于高流量应用来说,这种开销会不断累积。IronPDF的进程内方法在初始化后的开销可以忽略不计。
冷启动问题:容器启动可能会使首次请求延迟 2-5 秒。 即使是温暖的容器也有网络开销。 每次 pod 重启、扩展事件或部署都会触发冷启动。IronPDF的冷启动在每个应用程序生命周期内发生一次。
运维复杂性:高登堡需要将容器健康状况、扩展、日志记录和监控作为单独的问题进行管理。 网络超时、服务不可用和容器崩溃成为应用程序关注的问题。 IronPdf 使用标准的 .NET 异常处理。
Multipart 表单数据 API:每个高登堡请求都需要构建带有基于字符串的参数的 multipart/form-data 有效负载——冗长且没有编译时类型检查。IronPDF提供类型化的 C# 属性,并支持 IntelliSense。
版本管理:高登堡图片的更新与您的应用程序是分开进行的。 API 的更改可能会破坏集成。IronPDF版本通过 NuGet 与标准 .NET 依赖关系管理进行管理。
优势和考虑因素
高登堡的优势
-多语言架构:可与任何能够发起 HTTP 请求的语言配合使用 -语言无关:不局限于 .NET 生态系统
- MIT许可证:自由开源软件 微服务模式:适用于容器化架构
戈登堡注意事项
-基础设施开销:需要 Docker、Kubernetes 和负载均衡器。 -网络延迟:每次请求 10-100 毫秒以上 -冷启动:容器初始化时间为 2-5 秒 -基于字符串的 API:没有类型安全或智能感知功能 -分布式调试:需要分布式跟踪 -健康监测:需要管理的其他端点
IronPDF的优势
-零基础架构:仅 NuGet 包 -进程内性能:初始化后无网络延迟 -类型安全的 API:支持强类型属性和智能感知 -标准调试:普通的 .NET 调试器正常工作 -丰富的资源:大量的教程和文档 专业支持:商业许可包含支持服务
IronPDF注意事项
- .NET 专用:专为 .NET 生态系统设计 -商业许可:生产用途必需
Gotenberg 和IronPDF代表了在 .NET 应用程序中生成 PDF 的根本不同方法。高登堡基于 Docker 的微服务架构引入了容器管理、网络延迟和操作复杂性。 每个 PDF 操作都需要 HTTP 通信以及相关的故障模式和冷启动惩罚。
IronPDF 提供与进程内库一样的基于 Chromium 的渲染。 NuGet 软件包取消了 Docker 容器、网络调用和基础设施管理。 类型化的 C# API 取代了基于字符串的多部分表单数据。 标准的 .NET 异常处理取代了 HTTP 状态代码和网络故障模式。
随着企业对 .NET 10、C# 14 以及 2026 年之前的应用程序开发进行规划,在微服务基础架构开销和进程内库简易性之间做出选择会极大地影响部署和操作的复杂性。 寻求在保持 HTML/CSS/JavaScript 渲染保真度的同时减轻基础架构负担的团队会发现 IronPdf 能有效满足这些要求。