如何在 C# 中將 wkhtmltopdf 遷移到 IronPDF
wkhtmltopdf 是一款廣泛使用的工具,用於使用 Qt WebKit 將 HTML 文件轉換為 PDF。 儘管該專案因其命令列功能和免費許可而在開發者中廣受歡迎,但該專案現在存在著不容忽視的嚴重安全風險。 該庫已於 2016-2017 年正式棄用,並且存在一個嚴重漏洞(CVE-2022-35583),該漏洞至今仍未修復。
本指南提供了從 wkhtmltopdf 到 IronPDF 的完整遷移路徑,其中包含逐步說明、程式碼比較和實用範例,供需要從應用程式中消除此安全風險的專業 .NET 開發人員參考。
嚴重安全警告:CVE-2022-35583
wkhtmltopdf 包含一個永遠不會修復的嚴重安全漏洞:
| 問題 | 嚴重程度 | 地位 |
|---|---|---|
| CVE-2022-35583 | 評分:9.8/10 | 未打補丁 |
| SSRF漏洞 | 基礎設施接管風險 | 未打補丁 |
| 最後更新 | 2016-2017 | 棄 |
| WebKit 版本 | 2015 (Qt WebKit) | 過時的 |
| CSS Grid 支持 | 沒有任何 | 破碎的 |
| Flexbox 支援 | 部分的 | 破碎的 |
| ES6+ JavaScript | 沒有任何 | 破碎的 |
SSRF攻擊的工作原理
伺服器端請求偽造漏洞允許攻擊者存取內部服務、竊取憑證、掃描內部網絡,並透過精心建構的 HTML 程式碼竊取敏感資料:
<!-- Malicious HTML submitted to your PDF generator -->
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin"/><!-- Malicious HTML submitted to your PDF generator -->
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin"/>當 wkhtmltopdf 渲染此 HTML 時,它會從伺服器的網路上下文中取得這些 URL,從而繞過防火牆和安全控制。
受影響的包裝庫
所有用於 wkhtmltopdf 的 .NET 封裝庫都存在這些漏洞:
| 包裝庫 | 地位 | 安全風險 |
|---|---|---|
| DinkToPdf | 棄 | ⚠️ 重要提示 |
| 旋轉 | 棄 | ⚠️ 重要提示 |
| 週二佩奇金 | 棄 | ⚠️ 重要提示 |
| WkHtmlToPdf-DotNet | 棄 | ⚠️ 重要提示 |
| NReco.Pdf產生器 | 使用 wkhtmltopdf | ⚠️ 重要提示 |
如果您使用這些庫中的任何一個,您將容易受到 CVE-2022-35583 的攻擊。
IronPDF 與 wkhtmltopdf:功能對比
了解架構差異有助於技術決策者評估遷移投資:
| 特徵 | wkhtmltopdf | IronPDF |
|---|---|---|
| 授權 | LGPLv3(免費) | 商業的 |
| 渲染引擎 | Qt WebKit(2015) | 當前鉻引擎 |
| 安全漏洞 | CVE-2022-35583,有重大未修復問題 | 沒有已知的CVE |
| 主動維護 | 已棄坑,自 2017 年以來沒有實質更新。 | 持續維護,定期發布新版本。 |
| 支援現代網路標準 | 功能受限(flexbox 佈局損壞,不支援 CSS Grid) | 全力支持 |
| 整合與支援 | 僅限社群論壇 | 詳盡的文件和專門的支持 |
| CSS Grid | ❌ 不支持 | ✅ 全力支持 |
| Flexbox | ⚠️ 破損 | ✅ 全力支持 |
| ES6+ JavaScript | ❌ 不支持 | ✅ 全力支持 |
| 異步/等待 | ❌ 不支持 | ✅ 全力支持 |
| PDF 處理 | ❌ 不支持 | ✅ 全力支持 |
| 數位簽名 | ❌ 不支持 | ✅ 全力支持 |
| PDF/A 合規性 | ❌ 不支持 | ✅ 全力支持 |
快速入門:wkhtmltopdf 到 IronPDF 的遷移
透過這些基礎步驟,遷移工作可以立即開始。
步驟 1:刪除 wkhtmltopdf 軟體包和二進位文件
移除所有 wkhtmltopdf 封裝包:
# Remove wkhtmltopdf wrapper (whichever you're using)
dotnet remove package WkHtmlToPdf-DotNet
dotnet remove package DinkToPdf
dotnet remove package TuesPechkin
dotnet remove package Rotativa
dotnet remove package Rotativa.AspNetCore
dotnet remove package NReco.PdfGenerator
# Remove wkhtmltopdf binary from your deployment
# Delete wkhtmltopdf.exe, wkhtmltox.dll, etc.# Remove wkhtmltopdf wrapper (whichever you're using)
dotnet remove package WkHtmlToPdf-DotNet
dotnet remove package DinkToPdf
dotnet remove package TuesPechkin
dotnet remove package Rotativa
dotnet remove package Rotativa.AspNetCore
dotnet remove package NReco.PdfGenerator
# Remove wkhtmltopdf binary from your deployment
# Delete wkhtmltopdf.exe, wkhtmltox.dll, etc.步驟 2:安裝 IronPDF
# Add IronPDF (secure, modern alternative)
dotnet add package IronPdf# Add IronPDF (secure, modern alternative)
dotnet add package IronPdf步驟 3:更新命名空間
將 wkhtmltopdf 命名空間替換為 IronPdf 命名空間:
// Before (wkhtmltopdf)
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
// After (IronPDF)
using IronPdf;// Before (wkhtmltopdf)
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
// After (IronPDF)
using IronPdf;步驟 4:初始化許可證
在應用程式啟動時新增許可證初始化:
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";程式碼遷移範例
將 HTML 轉換為 PDF
最基本的操作揭示了這些 .NET PDF 方法之間的複雜性差異。
wkhtmltopdf 方法:
// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
方向 = Orientation.Portrait,
PaperSize = PaperKind.A4
},
Objects = {
new ObjectSettings()
{
HtmlContent = "<h1>Hello World</h1><p>This is a PDF from HTML.</p>"
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("output.pdf", pdf);
}
}// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
方向 = Orientation.Portrait,
PaperSize = PaperKind.A4
},
Objects = {
new ObjectSettings()
{
HtmlContent = "<h1>Hello World</h1><p>This is a PDF from HTML.</p>"
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("output.pdf", pdf);
}
}IronPDF 方法:
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF from HTML.</p>");
pdf.SaveAs("output.pdf");
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF from HTML.</p>");
pdf.SaveAs("output.pdf");
}
}wkhtmltopdf 需要使用PdfTools建立一個SynchronizedConverter ,使用GlobalSettings和Objects建構一個HtmlToPdfDocument ,設定ColorMode 、 Orientation和PaperSize等屬性,呼叫converter.Convert()取得原始位元組,然後使用File.WriteAllBytes()手動寫入檔案。
IronPDF 完全消除了這個步驟-建立一個ChromePdfRenderer ,呼叫RenderHtmlAsPdf() ,然後使用內建的SaveAs()方法。
如需更進階的 HTML 轉 PDF 場景,請參閱HTML 轉 PDF 轉換指南。
將 URL 轉換為 PDF
URL 轉 PDF 轉換也呈現類似的複雜性模式。
wkhtmltopdf 方法:
// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
方向 = Orientation.Portrait,
PaperSize = PaperKind.A4
},
Objects = {
new ObjectSettings()
{
Page = "https://www.example.com"
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("webpage.pdf", pdf);
}
}// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
方向 = Orientation.Portrait,
PaperSize = PaperKind.A4
},
Objects = {
new ObjectSettings()
{
Page = "https://www.example.com"
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("webpage.pdf", pdf);
}
}IronPDF 方法:
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
}
}wkhtmltopdf 使用ObjectSettings中的Page屬性來指定 URL,這需要相同的文件建構模式。 IronPDF 提供了一個專門的RenderUrlAsPdf()方法,可以清楚地表達意圖。
查看指向 PDF 文件的 URL ,以了解身份驗證和自訂標頭選項。
自訂設定:包含頁面配置的 HTML 文件
配置方向、邊距和紙張尺寸需要不同的方法。
wkhtmltopdf 方法:
// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
方向 = Orientation.Landscape,
PaperSize = PaperKind.A4,
Margins = new MarginSettings() { Top = 10, Bottom = 10, Left = 10, Right = 10 }
},
Objects = {
new ObjectSettings()
{
Page = "input.html",
WebSettings = { DefaultEncoding = "utf-8" }
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("custom-output.pdf", pdf);
}
}// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;
class Program
{
static void Main()
{
var converter = new SynchronizedConverter(new PdfTools());
var doc = new HtmlToPdfDocument()
{
GlobalSettings = {
ColorMode = ColorMode.Color,
方向 = Orientation.Landscape,
PaperSize = PaperKind.A4,
Margins = new MarginSettings() { Top = 10, Bottom = 10, Left = 10, Right = 10 }
},
Objects = {
new ObjectSettings()
{
Page = "input.html",
WebSettings = { DefaultEncoding = "utf-8" }
}
}
};
byte[] pdf = converter.Convert(doc);
File.WriteAllBytes("custom-output.pdf", pdf);
}
}IronPDF 方法:
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 10;
renderer.RenderingOptions.MarginRight = 10;
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
var pdf = renderer.RenderHtmlFileAsPdf("input.html");
pdf.SaveAs("custom-output.pdf");
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 10;
renderer.RenderingOptions.MarginRight = 10;
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
var pdf = renderer.RenderHtmlFileAsPdf("input.html");
pdf.SaveAs("custom-output.pdf");
}
}wkhtmltopdf 將設定嵌套在GlobalSettings和Objects中,其中MarginSettings是一個單獨的物件。 IronPDF 提供直接的RenderingOptions屬性,其名稱清晰明了,例如PaperOrientation 、 MarginTop和PaperSize 。
wkhtmltopdf API 到 IronPDF 映射參考
此映射透過顯示直接的 API 等效項來加速遷移:
CLI 到 IronPDF 映射
| wkhtmltopdf CLI 選項 | IronPDF當量 | 筆記 |
|---|---|---|
wkhtmltopdf input.html output.pdf | renderer.RenderHtmlFileAsPdf() | 文件轉換為 PDF |
wkhtmltopdf URL output.pdf | renderer.RenderUrlAsPdf() | PDF檔案的URL |
--page-size A4 | RenderingOptions.PaperSize = PdfPaperSize.A4 | 紙張尺寸 |
--page-size Letter | RenderingOptions.PaperSize = PdfPaperSize.Letter | 美國信紙 |
--orientation Landscape | RenderingOptions.PaperOrientation = Landscape | 方向 |
--margin-top 10mm | RenderingOptions.MarginTop = 10 | 邊距(毫米) |
--margin-bottom 10mm | RenderingOptions.MarginBottom = 10 | |
--margin-left 10mm | RenderingOptions.MarginLeft = 10 | |
--margin-right 10mm | RenderingOptions.MarginRight = 10 | |
--header-html header.html | RenderingOptions.HtmlHeader | HTML 頭部 |
--footer-center "[page]" | {page}佔位符 | 頁碼 |
--footer-center "[toPage]" | {total-pages}佔位符 | 總頁數 |
--enable-javascript | 預設啟用 | JavaScript |
--javascript-delay 500 | RenderingOptions.WaitFor.RenderDelay = 500 | JS 延遲 |
--dpi 300 | RenderingOptions.Dpi = 300 | DPI設定 |
--grayscale | RenderingOptions.GrayScale = true | 灰階 |
C# 封裝器 API 映射
| wkhtmltopdf 包裝器 | IronPDF | 筆記 |
|---|---|---|
SynchronizedConverter | ChromePdfRenderer | 主渲染器 |
HtmlToPdfDocument | RenderingOptions | 配置 |
GlobalSettings.Out | pdf.SaveAs() | 輸出檔案 |
GlobalSettings.PaperSize | RenderingOptions.PaperSize | 紙張尺寸 |
GlobalSettings.Orientation | RenderingOptions.PaperOrientation | 方向 |
GlobalSettings.Margins | RenderingOptions.Margin* | 個人利潤 |
ObjectSettings.Page | RenderHtmlFileAsPdf() | 文件輸入 |
ObjectSettings.HtmlContent | RenderHtmlAsPdf() | HTML字串 |
converter.Convert(doc) | renderer.RenderHtmlAsPdf() | 產生 PDF |
佔位符語法遷移
| wkhtmltopdf佔位符 | IronPDF佔位符 |
|---|---|
[page] | {page} |
[toPage] | {total-pages} |
[date] | {date} |
[time] | {time} |
[title] | {html-title} |
[url] | {url} |
常見遷移問題及解決方案
問題 1:頁首/頁尾佔位符語法
wkhtmltopdf:使用方括號語法,如[page]和[toPage] 。
解決方案:更新 IronPDF 的花括號佔位符:
// Before (wkhtmltopdf)
FooterSettings = { Left = "Page [page] of [toPage]" }
// After (IronPDF)
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
HtmlFragment = "<div style='text-align:left;'>Page {page} of {total-pages}</div>",
MaxHeight = 25
};// Before (wkhtmltopdf)
FooterSettings = { Left = "Page [page] of [toPage]" }
// After (IronPDF)
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
HtmlFragment = "<div style='text-align:left;'>Page {page} of {total-pages}</div>",
MaxHeight = 25
};問題 2:JavaScript 延遲配置
wkhtmltopdf:使用JavascriptDelay屬性,但可靠性有限。
解決方案: IronPDF 提供多種選擇:
renderer.RenderingOptions.EnableJavaScript = true;
// Option 1: Fixed delay
renderer.RenderingOptions.WaitFor.RenderDelay(500);
// Option 2: Wait for specific element (more reliable)
renderer.RenderingOptions.WaitFor.HtmlElementById("content-loaded");
// Option 3: Wait for JavaScript condition
renderer.RenderingOptions.WaitFor.JavaScript("window.renderComplete === true");renderer.RenderingOptions.EnableJavaScript = true;
// Option 1: Fixed delay
renderer.RenderingOptions.WaitFor.RenderDelay(500);
// Option 2: Wait for specific element (more reliable)
renderer.RenderingOptions.WaitFor.HtmlElementById("content-loaded");
// Option 3: Wait for JavaScript condition
renderer.RenderingOptions.WaitFor.JavaScript("window.renderComplete === true");問題 3:現代 CSS 無法渲染
症狀: wkhtmltopdf 中 CSS Grid 和 Flexbox 佈局渲染不正確。
解決方案: IronPDF 的 Chromium 引擎可以正確處理現代 CSS:
// This CSS now works with IronPDF
var html = @"
<style>
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.flex { display: flex; justify-content: space-between; align-items: center; }
</style>
<div class='grid'>
<div>Column 1</div>
<div>Column 2</div>
<div>Column 3</div>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html);// This CSS now works with IronPDF
var html = @"
<style>
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.flex { display: flex; justify-content: space-between; align-items: center; }
</style>
<div class='grid'>
<div>Column 1</div>
<div>Column 2</div>
<div>Column 3</div>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html);問題 4:同步渲染與非同步渲染
wkhtmltopdf:包裝器是同步的,會阻塞執行緒。
解決方案: IronPDF 支援非同步渲染:
public async Task<byte[]> GeneratePdfAsync(string html)
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return pdf.BinaryData;
}public async Task<byte[]> GeneratePdfAsync(string html)
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderHtmlAsPdfAsync(html);
return pdf.BinaryData;
}wkhtmltopdf 遷移清單
遷移前任務
審核您的程式碼庫,找出所有 wkhtmltopdf 的使用情況:
# Find all wkhtmltopdf references
grep -r "WkHtmlToPdfDotNet\|DinkToPdf\|TuesPechkin\|Rotativa" --include="*.cs" .
grep -r "wkhtmltopdf" --include="*.yml" --include="*.yaml" --include="Dockerfile" .# Find all wkhtmltopdf references
grep -r "WkHtmlToPdfDotNet\|DinkToPdf\|TuesPechkin\|Rotativa" --include="*.cs" .
grep -r "wkhtmltopdf" --include="*.yml" --include="*.yaml" --include="Dockerfile" .尋找並記錄 wkhtmltopdf 二進位檔案以便刪除。 記錄目前設定(紙張大小、頁邊距、頁首/頁尾)。
程式碼更新任務
- 刪除所有 wkhtmltopdf 包裝 NuGet 套件
- 刪除 wkhtmltopdf 二進位(wkhtmltopdf.exe、wkhtmltox.dll)
- 安裝 IronPdf NuGet 套件
- 將命名空間導入從
WkHtmlToPdfDotNet更新為IronPdf - 將
SynchronizedConverter替換為ChromePdfRenderer - 將
HtmlToPdfDocument模式轉換為直接渲染方法 - 將
GlobalSettings配置更新為RenderingOptions - 將邊距配置從
MarginSettings轉換為單獨的屬性 - 更新佔位符語法(
[page]→{page},[toPage]→{total-pages}) - 在啟動時加入 IronPDF 許可證初始化
遷移後測試
遷移完成後,請確認以下幾個面向:
- PDF 輸出的視覺對比(隨著現代 CSS 支援的應用,效果應該會更好)
- 驗證現代 CSS 渲染(CSS Grid 和 Flexbox 現在可以正常工作)
- 測試大量使用 JavaScript 的頁面
- 安全性掃描以確認沒有殘留的 wkhtmltopdf 二進位。
- 用於效能比較的負載測試
安全驗證
# Scan for any remaining wkhtmltopdf artifacts
find /var/www/ -name "*wkhtmlto*" 2>/dev/null
find /usr/local/bin/ -name "*wkhtmlto*" 2>/dev/null
docker images | grep wkhtmltopdf
# Check if any process is still using it
ps aux | grep wkhtmltopdf# Scan for any remaining wkhtmltopdf artifacts
find /var/www/ -name "*wkhtmlto*" 2>/dev/null
find /usr/local/bin/ -name "*wkhtmlto*" 2>/dev/null
docker images | grep wkhtmltopdf
# Check if any process is still using it
ps aux | grep wkhtmltopdf遷移到 IronPDF 的主要優勢
從 wkhtmltopdf 遷移到 IronPDF 可以帶來以下幾個關鍵優勢:
安全性: CVE-2022-35583 和所有 wkhtmltopdf 漏洞均已消除。 IronPDF 沒有已知的 CVE 漏洞,並定期收到安全性更新。
現代渲染引擎: IronPDF 使用最新的 Chromium 引擎,確保完全支援 CSS3、CSS Grid、Flexbox 和 ES6+ JavaScript。 現代框架能夠正確渲染。
簡化 API:直接渲染方法取代文件建構模式。 內建的SaveAs()方法無需手動處理位元組。
擴充功能: PDF 操作、數位簽章、PDF/A 合規性、浮水印和合併/分割操作是 wkhtmltopdf 無法提供的內建功能。
積極開發:隨著 .NET 10 和 C# 14 的普及,IronPDF 將持續更新,確保與目前和未來的 .NET 版本相容。
非同步支援:透過原生 async/await 支持,防止高負載 Web 應用程式中的執行緒阻塞。






