跳至页脚内容
使用IRONPDF

如何通过C#将PDF文件作为电子邮件附件发送

自动化文档交付几乎是每个业务线 .NET 应用程序都会遇到的需求。 订单生成后,发票必须在几秒钟内送达客户。 如果报告是在夜间生成的,利益相关者希望在上班前就能在邮箱里收到。最简单、最通用的交付格式是将PDF文件作为电子邮件附件发送。 本指南将引导您完成 C# 中的完整端到端工作流程——使用IronPDF在内存中生成 PDF 文档,然后使用 MailKit 或内置的 System.Net.Mail 命名空间将其作为电子邮件附件发送,所有这些都无需向磁盘写入一个字节。

如何安装所需的软件包?

该工作流程由两个软件包驱动:PDF 生成库和电子邮件发送库。 可以通过 Visual Studio 中的程序包管理器控制台或通过 .NET CLI 安装两者。

Install-Package IronPdf
dotnet add package IronPdf
Install-Package MailKit
dotnet add package MailKit
Install-Package IronPdf
dotnet add package IronPdf
Install-Package MailKit
dotnet add package MailKit
SHELL

IronPDF 采用基于 Chromium 的渲染引擎,可将 HTML、CSS 和 JavaScript 转换为像素级完美的 PDF 文档。 它可以运行在 Windows、Linux 和 macOS 上,这意味着相同的代码可以在 ASP.NET Core Web API、后台服务或 Azure 函数中使用。 MailKit 是微软推荐用于所有新的 .NET 电子邮件开发的库——它支持 SMTP、IMAP、POP3、OAuth 2.0 和完整的 MIME 构造。 MailKit 的源代码和文档可在 GitHub 上找到。

如何在内存中生成 PDF 文档?

ChromePdfRendererHTML 到 PDF 转换的入口点。 将 HTML 字符串传递给 RenderHtmlAsPdf,您将得到一个完全驻留在内存中的 PdfDocument 对象。 通过 BinaryData 属性访问原始字节 -- 此字节数组正是电子邮件附件 API 所期望的。

using IronPdf;

var renderer = new ChromePdfRenderer();

string htmlContent = """
    <h1>Order Confirmation</h1>
    <p>Thank you for your purchase.</p>
    <table>
        <tr><th>Item</th><th>Qty</th><th>Price</th></tr>
        <tr><td>Widget A</td><td>2</td><td>$19.99</td></tr>
        <tr><td>Widget B</td><td>1</td><td>$59.99</td></tr>
    </table>
    <p><strong>Order Total: $99.97</strong></p>
    """;

PdfDocument pdf = renderer.RenderHtmlAsPdf(htmlContent);

// pdf.BinaryData holds the complete PDF as a byte array
byte[] pdfBytes = pdf.BinaryData;
Console.WriteLine($"PDF generated: {pdfBytes.Length} bytes");
using IronPdf;

var renderer = new ChromePdfRenderer();

string htmlContent = """
    <h1>Order Confirmation</h1>
    <p>Thank you for your purchase.</p>
    <table>
        <tr><th>Item</th><th>Qty</th><th>Price</th></tr>
        <tr><td>Widget A</td><td>2</td><td>$19.99</td></tr>
        <tr><td>Widget B</td><td>1</td><td>$59.99</td></tr>
    </table>
    <p><strong>Order Total: $99.97</strong></p>
    """;

PdfDocument pdf = renderer.RenderHtmlAsPdf(htmlContent);

// pdf.BinaryData holds the complete PDF as a byte array
byte[] pdfBytes = pdf.BinaryData;
Console.WriteLine($"PDF generated: {pdfBytes.Length} bytes");
Imports IronPdf

Dim renderer As New ChromePdfRenderer()

Dim htmlContent As String = "
    <h1>Order Confirmation</h1>
    <p>Thank you for your purchase.</p>
    <table>
        <tr><th>Item</th><th>Qty</th><th>Price</th></tr>
        <tr><td>Widget A</td><td>2</td><td>$19.99</td></tr>
        <tr><td>Widget B</td><td>1</td><td>$59.99</td></tr>
    </table>
    <p><strong>Order Total: $99.97</strong></p>
    "

Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(htmlContent)

' pdf.BinaryData holds the complete PDF as a byte array
Dim pdfBytes As Byte() = pdf.BinaryData
Console.WriteLine($"PDF generated: {pdfBytes.Length} bytes")
$vbLabelText   $csharpLabel

RenderHtmlAsPdf 方法使用与 Google Chrome 相同的 Chromium 引擎解析 HTML,因此表格、CSS Grid、Flexbox 和嵌入式字体都与在浏览器中呈现的效果完全相同。 结果是一个 PdfDocument,其 BinaryData 属性返回完整的 PDF 二进制文件,无需任何磁盘读取和写入操作。 对于引入外部图像或样式表的文档,可以使用可选的 BasePath 参数来告诉 IronPDF 在哪里解析相对资源 URL——这在HTML 文件到 PDF 的操作方法页面上有详细介绍。

设置页面布局和自定义页眉

在附加 PDF 文件之前,您可能需要配置页边距、页眉或页脚。 所有布局选项都位于 RenderingOptions 属性中:

using IronPdf;

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginTop    = 15;
renderer.RenderingOptions.MarginBottom = 15;
renderer.RenderingOptions.MarginLeft   = 12;
renderer.RenderingOptions.MarginRight  = 12;

renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='font-size:9pt;color:#666;text-align:right;'>Monthly Report</div>",
    DrawDividerLine = true
};

renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='font-size:8pt;text-align:center;'>{page} of {total-pages}</div>"
};

PdfDocument pdf = renderer.RenderHtmlAsPdf("<h1>Monthly Summary</h1><p>See attached data.</p>");
byte[] pdfBytes = pdf.BinaryData;
using IronPdf;

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginTop    = 15;
renderer.RenderingOptions.MarginBottom = 15;
renderer.RenderingOptions.MarginLeft   = 12;
renderer.RenderingOptions.MarginRight  = 12;

renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='font-size:9pt;color:#666;text-align:right;'>Monthly Report</div>",
    DrawDividerLine = true
};

renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='font-size:8pt;text-align:center;'>{page} of {total-pages}</div>"
};

PdfDocument pdf = renderer.RenderHtmlAsPdf("<h1>Monthly Summary</h1><p>See attached data.</p>");
byte[] pdfBytes = pdf.BinaryData;
Imports IronPdf

Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.MarginTop = 15
renderer.RenderingOptions.MarginBottom = 15
renderer.RenderingOptions.MarginLeft = 12
renderer.RenderingOptions.MarginRight = 12

renderer.RenderingOptions.HtmlHeader = New HtmlHeaderFooter With {
    .HtmlFragment = "<div style='font-size:9pt;color:#666;text-align:right;'>Monthly Report</div>",
    .DrawDividerLine = True
}

renderer.RenderingOptions.HtmlFooter = New HtmlHeaderFooter With {
    .HtmlFragment = "<div style='font-size:8pt;text-align:center;'>{page} of {total-pages}</div>"
}

Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf("<h1>Monthly Summary</h1><p>See attached data.</p>")
Dim pdfBytes As Byte() = pdf.BinaryData
$vbLabelText   $csharpLabel

边距单位为毫米。 {page}{total-pages} 标记会在渲染时被替换。HTML字符串转 PDF 的操作指南页面涵盖了所有渲染选项。 您还可以在附加之前,在生成的文档上添加水印图文印章

如何使用 MailKit 将 PDF 文件附加到电子邮件中?

MailKit 直接构建 MIME 邮件树,让您可以完全控制内容类型、编码和附件元数据。 BodyBuilder 辅助类简化了带有一个或多个文件附件的文本或 HTML 正文的常见情况。

using IronPdf;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;

// Step 1 -- generate the PDF in memory
var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf("<h1>Monthly Report</h1><p>Generated automatically.</p>");

// Step 2 -- build the email message
var message = new MimeMessage();
message.From.Add(new MailboxAddress("Reports Service", "reports@example.com"));
message.To.Add(new MailboxAddress("Alice Smith", "alice@example.com"));
message.Subject = "Your Monthly Report is Ready";

var builder = new BodyBuilder();
builder.TextBody = "Hello Alice,\n\nPlease find your monthly report attached.\n\nRegards,\nReports Service";
builder.HtmlBody = "<p>Hello Alice,</p><p>Please find your monthly report attached.</p>";

// Add the in-memory PDF as an attachment
builder.Attachments.Add("MonthlyReport.pdf", pdf.BinaryData, new ContentType("application", "pdf"));
message.Body = builder.ToMessageBody();

// Step 3 -- send via SMTP with TLS
using var client = new SmtpClient();
await client.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls);
await client.AuthenticateAsync("username", "app-password");
await client.SendAsync(message);
await client.DisconnectAsync(true);
using IronPdf;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;

// Step 1 -- generate the PDF in memory
var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf("<h1>Monthly Report</h1><p>Generated automatically.</p>");

// Step 2 -- build the email message
var message = new MimeMessage();
message.From.Add(new MailboxAddress("Reports Service", "reports@example.com"));
message.To.Add(new MailboxAddress("Alice Smith", "alice@example.com"));
message.Subject = "Your Monthly Report is Ready";

var builder = new BodyBuilder();
builder.TextBody = "Hello Alice,\n\nPlease find your monthly report attached.\n\nRegards,\nReports Service";
builder.HtmlBody = "<p>Hello Alice,</p><p>Please find your monthly report attached.</p>";

// Add the in-memory PDF as an attachment
builder.Attachments.Add("MonthlyReport.pdf", pdf.BinaryData, new ContentType("application", "pdf"));
message.Body = builder.ToMessageBody();

// Step 3 -- send via SMTP with TLS
using var client = new SmtpClient();
await client.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls);
await client.AuthenticateAsync("username", "app-password");
await client.SendAsync(message);
await client.DisconnectAsync(true);
Imports IronPdf
Imports MailKit.Net.Smtp
Imports MailKit.Security
Imports MimeKit

' Step 1 -- generate the PDF in memory
Dim renderer As New ChromePdfRenderer()
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf("<h1>Monthly Report</h1><p>Generated automatically.</p>")

' Step 2 -- build the email message
Dim message As New MimeMessage()
message.From.Add(New MailboxAddress("Reports Service", "reports@example.com"))
message.To.Add(New MailboxAddress("Alice Smith", "alice@example.com"))
message.Subject = "Your Monthly Report is Ready"

Dim builder As New BodyBuilder()
builder.TextBody = "Hello Alice," & vbCrLf & vbCrLf & "Please find your monthly report attached." & vbCrLf & vbCrLf & "Regards," & vbCrLf & "Reports Service"
builder.HtmlBody = "<p>Hello Alice,</p><p>Please find your monthly report attached.</p>"

' Add the in-memory PDF as an attachment
builder.Attachments.Add("MonthlyReport.pdf", pdf.BinaryData, New ContentType("application", "pdf"))
message.Body = builder.ToMessageBody()

' Step 3 -- send via SMTP with TLS
Using client As New SmtpClient()
    Await client.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls)
    Await client.AuthenticateAsync("username", "app-password")
    Await client.SendAsync(message)
    Await client.DisconnectAsync(True)
End Using
$vbLabelText   $csharpLabel

builder.Attachments.Add 接受三个参数:收件人在其电子邮件客户端中看到的文件名、来自 pdf.BinaryData 的原始字节数组,以及指定 MIME 类型为 application/pdfContentType 实例。 异步 SMTP 方法在网络操作完成时保持调用线程空闲——这对于处理数十个并发请求的 ASP.NET Core 控制器至关重要。

SecureSocketOptions.StartTls 与 SMTP 服务器在 587 端口上协商加密通道。对于 Gmail,请使用应用密码而不是您的帐户密码:在 Google 帐户安全 > 应用密码下生成一个应用密码,然后将其传递给 AuthenticateAsync。 对于 Microsoft 365,如果您的租户已禁用基本身份验证,请通过 MailKit 的 SaslMechanismOAuth2 类配置 OAuth 2.0 身份验证。

发送给多个收件人

要将多个人抄送至同一封电子邮件,请在调用 SendAsync 之前,将地址添加到 ToCcBcc 集合中:

message.To.Add(new MailboxAddress("Alice Smith",   "alice@example.com"));
message.To.Add(new MailboxAddress("Bob Jones",     "bob@example.com"));
message.Cc.Add(new MailboxAddress("Carol Manager", "carol@example.com"));
message.To.Add(new MailboxAddress("Alice Smith",   "alice@example.com"));
message.To.Add(new MailboxAddress("Bob Jones",     "bob@example.com"));
message.Cc.Add(new MailboxAddress("Carol Manager", "carol@example.com"));
message.To.Add(New MailboxAddress("Alice Smith", "alice@example.com"))
message.To.Add(New MailboxAddress("Bob Jones", "bob@example.com"))
message.Cc.Add(New MailboxAddress("Carol Manager", "carol@example.com"))
$vbLabelText   $csharpLabel

一次 SendAsync 调用即可将消息发送到所有地址。 MailKit 在一个 SMTP 会话中批量处理 RCPT TO 命令,因此对于多个收件人不会造成性能损失。

如何使用 System.Net.Mail 作为替代方案?

对于面向旧版 .NET 的项目或不允许添加第三方 NuGet 包的项目,内置的 System.Net.Mail 命名空间处理基本的 SMTP 传递。 微软不再建议在新开发项目中使用它,但它涵盖了常见的用例,而且没有额外的依赖项。

using IronPdf;
using System.Net;
using System.Net.Mail;

// Generate the PDF
var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf("<h1>Invoice #1001</h1><p>Amount due: $350.00</p>");

// Build the mail message
using var message = new MailMessage("invoices@example.com", "customer@example.com");
message.Subject = "Invoice #1001 Attached";
message.Body    = "Your invoice is attached to this email. Please remit payment within 30 days.";
message.IsBodyHtml = false;

// Wrap the byte array in a MemoryStream for the Attachment constructor
var stream = new MemoryStream(pdf.BinaryData);
message.Attachments.Add(new Attachment(stream, "Invoice-1001.pdf", "application/pdf"));

// Send via SMTP
using var client = new SmtpClient("smtp.example.com", 587)
{
    Credentials = new NetworkCredential("username", "password"),
    EnableSsl   = true
};
await client.SendMailAsync(message);
using IronPdf;
using System.Net;
using System.Net.Mail;

// Generate the PDF
var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf("<h1>Invoice #1001</h1><p>Amount due: $350.00</p>");

// Build the mail message
using var message = new MailMessage("invoices@example.com", "customer@example.com");
message.Subject = "Invoice #1001 Attached";
message.Body    = "Your invoice is attached to this email. Please remit payment within 30 days.";
message.IsBodyHtml = false;

// Wrap the byte array in a MemoryStream for the Attachment constructor
var stream = new MemoryStream(pdf.BinaryData);
message.Attachments.Add(new Attachment(stream, "Invoice-1001.pdf", "application/pdf"));

// Send via SMTP
using var client = new SmtpClient("smtp.example.com", 587)
{
    Credentials = new NetworkCredential("username", "password"),
    EnableSsl   = true
};
await client.SendMailAsync(message);
Imports IronPdf
Imports System.Net
Imports System.Net.Mail
Imports System.IO

' Generate the PDF
Dim renderer As New ChromePdfRenderer()
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf("<h1>Invoice #1001</h1><p>Amount due: $350.00</p>")

' Build the mail message
Using message As New MailMessage("invoices@example.com", "customer@example.com")
    message.Subject = "Invoice #1001 Attached"
    message.Body = "Your invoice is attached to this email. Please remit payment within 30 days."
    message.IsBodyHtml = False

    ' Wrap the byte array in a MemoryStream for the Attachment constructor
    Dim stream As New MemoryStream(pdf.BinaryData)
    message.Attachments.Add(New Attachment(stream, "Invoice-1001.pdf", "application/pdf"))

    ' Send via SMTP
    Using client As New SmtpClient("smtp.example.com", 587)
        client.Credentials = New NetworkCredential("username", "password")
        client.EnableSsl = True
        Await client.SendMailAsync(message)
    End Using
End Using
$vbLabelText   $csharpLabel

与 MailKit 的主要区别在于,System.Net.Mail.Attachment 不直接接受字节数组——您必须先将 pdf.BinaryData 包装在 MemoryStream 中。MailMessageSmtpClient 都包装在 using 语句中,这些语句会在发送邮件后释放 SMTP 连接并刷新底层流。 如果在 MailMessage 中省略 using,则在某些运行时,附件流可能会在发送完成之前被释放。

在 MailKit 和 System.Net.Mail 之间进行选择

MailKit 与 System.Net.Mail 功能比较
特征 MailKit System.Net.Mail
OAuth 2.0 身份验证
IMAP/POP3 支持
异步优先 API 部分的
微软推荐 受到推崇的 遗产
附加 NuGet 包 必需的 无需
复杂的 MIME 构造 全面支持 基础

对于任何新项目,请选择 MailKit,尤其是在 SMTP 服务器需要 OAuth 或需要 IMAP 读取回复的情况下。 当代码库已经依赖于 System.Net.Mail 且迁移成本不合理时,请使用 System.Net.Mail

如何将此模式应用于实际业务工作流程?

内存中 PDF 转电子邮件模式直接适用于驱动大多数业务应用程序的文档自动化场景。

发票自动化

电子商务订单处理程序会在收到付款后立即生成发票 PDF 文件。 OrderConfirmed 事件触发一个方法,该方法使用从订单数据填充的 Razor 模板 HTML 字符串调用 RenderHtmlAsPdf,然后将结果发送到客户的电子邮件地址。 由于 PDF 文件从未触及文件系统,因此不会留下任何需要清理的文件,不会出现共享临时目录上的竞争条件,也不会出现容器化部署中的权限问题。 有关从 Razor 视图渲染 HTML 的更多信息,请参阅ASP.NET Core PDF 生成指南

定期报告分发

使用 IHostedService 安排的后台服务会在每周一 06:00 生成每周分析摘要。 它查询数据库,构建 HTML 报告字符串,使用 IronPDF 渲染,并使用 MailKit 将其发送到邮件列表。整个流程以异步工作流的形式运行,因此在 SMTP 握手期间不会占用线程池线程。 对于 Azure 托管的工作负载, Azure PDF 生成器指南解释了如何在 Azure 应用服务和 Azure Functions 中部署 IronPDF。

在 ASP.NET Core 中生成收据

在 ASP.NET Core 最小 API 或控制器操作中,POST 端点接收结账有效负载,生成收据 PDF,并返回 HTTP 200,同时发送电子邮件。 将邮件发送逻辑放在后台运行,以便 HTTP 响应立即返回给客户端:

app.MapPost("/checkout", async (CheckoutRequest req, IEmailService emailService) =>
{
    var renderer = new ChromePdfRenderer();
    PdfDocument receipt = renderer.RenderHtmlAsPdf(BuildReceiptHtml(req));

    // Fire and forget -- do not await so the HTTP response is immediate
    _ = emailService.SendReceiptAsync(req.CustomerEmail, receipt.BinaryData);

    return Results.Ok(new { message = "Order confirmed." });
});
app.MapPost("/checkout", async (CheckoutRequest req, IEmailService emailService) =>
{
    var renderer = new ChromePdfRenderer();
    PdfDocument receipt = renderer.RenderHtmlAsPdf(BuildReceiptHtml(req));

    // Fire and forget -- do not await so the HTTP response is immediate
    _ = emailService.SendReceiptAsync(req.CustomerEmail, receipt.BinaryData);

    return Results.Ok(new { message = "Order confirmed." });
});
Imports System.Threading.Tasks

app.MapPost("/checkout", Async Function(req As CheckoutRequest, emailService As IEmailService) As Task(Of IResult)
    Dim renderer As New ChromePdfRenderer()
    Dim receipt As PdfDocument = renderer.RenderHtmlAsPdf(BuildReceiptHtml(req))

    ' Fire and forget -- do not await so the HTTP response is immediate
    _ = emailService.SendReceiptAsync(req.CustomerEmail, receipt.BinaryData)

    Return Results.Ok(New With {.message = "Order confirmed."})
End Function)
$vbLabelText   $csharpLabel

即使 SMTP 服务器速度较慢,也能将 API 响应时间保持在 100 毫秒以下。 emailService 注册为作用域或临时服务,包装了 MailKit SmtpClient

如何处理错误和重试?

网络运行失败。 SMTP 服务器暂时不可用,身份验证令牌过期,附件大小限制因提供商而异。 从一开始就在电子邮件发送流程中建立弹性。

将 MailKit 的发送逻辑封装在 try/catch 语句中,并将失败情况记录到持久队列中,以便可以重试:

using IronPdf;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using Microsoft.Extensions.Logging;

async Task SendPdfEmailWithRetryAsync(
    byte[] pdfBytes,
    string recipientEmail,
    string subject,
    ILogger logger,
    int maxAttempts = 3)
{
    for (int attempt = 1; attempt <= maxAttempts; attempt++)
    {
        try
        {
            var message = new MimeMessage();
            message.From.Add(new MailboxAddress("Mailer", "mailer@example.com"));
            message.To.Add(MailboxAddress.Parse(recipientEmail));
            message.Subject = subject;

            var builder = new BodyBuilder { TextBody = "Your document is attached." };
            builder.Attachments.Add("document.pdf", pdfBytes, new ContentType("application", "pdf"));
            message.Body = builder.ToMessageBody();

            using var smtpClient = new SmtpClient();
            await smtpClient.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls);
            await smtpClient.AuthenticateAsync("user", "pass");
            await smtpClient.SendAsync(message);
            await smtpClient.DisconnectAsync(true);

            logger.LogInformation("Email sent to {Email} on attempt {Attempt}", recipientEmail, attempt);
            return;
        }
        catch (Exception ex) when (attempt < maxAttempts)
        {
            logger.LogWarning(ex, "Send attempt {Attempt} failed. Retrying...", attempt);
            await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
        }
    }
}
using IronPdf;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using Microsoft.Extensions.Logging;

async Task SendPdfEmailWithRetryAsync(
    byte[] pdfBytes,
    string recipientEmail,
    string subject,
    ILogger logger,
    int maxAttempts = 3)
{
    for (int attempt = 1; attempt <= maxAttempts; attempt++)
    {
        try
        {
            var message = new MimeMessage();
            message.From.Add(new MailboxAddress("Mailer", "mailer@example.com"));
            message.To.Add(MailboxAddress.Parse(recipientEmail));
            message.Subject = subject;

            var builder = new BodyBuilder { TextBody = "Your document is attached." };
            builder.Attachments.Add("document.pdf", pdfBytes, new ContentType("application", "pdf"));
            message.Body = builder.ToMessageBody();

            using var smtpClient = new SmtpClient();
            await smtpClient.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls);
            await smtpClient.AuthenticateAsync("user", "pass");
            await smtpClient.SendAsync(message);
            await smtpClient.DisconnectAsync(true);

            logger.LogInformation("Email sent to {Email} on attempt {Attempt}", recipientEmail, attempt);
            return;
        }
        catch (Exception ex) when (attempt < maxAttempts)
        {
            logger.LogWarning(ex, "Send attempt {Attempt} failed. Retrying...", attempt);
            await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
        }
    }
}
Imports IronPdf
Imports MailKit.Net.Smtp
Imports MailKit.Security
Imports MimeKit
Imports Microsoft.Extensions.Logging

Public Async Function SendPdfEmailWithRetryAsync(
    pdfBytes As Byte(),
    recipientEmail As String,
    subject As String,
    logger As ILogger,
    Optional maxAttempts As Integer = 3) As Task

    For attempt As Integer = 1 To maxAttempts
        Try
            Dim message = New MimeMessage()
            message.From.Add(New MailboxAddress("Mailer", "mailer@example.com"))
            message.To.Add(MailboxAddress.Parse(recipientEmail))
            message.Subject = subject

            Dim builder = New BodyBuilder With {
                .TextBody = "Your document is attached."
            }
            builder.Attachments.Add("document.pdf", pdfBytes, New ContentType("application", "pdf"))
            message.Body = builder.ToMessageBody()

            Using smtpClient = New SmtpClient()
                Await smtpClient.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls)
                Await smtpClient.AuthenticateAsync("user", "pass")
                Await smtpClient.SendAsync(message)
                Await smtpClient.DisconnectAsync(True)
            End Using

            logger.LogInformation("Email sent to {Email} on attempt {Attempt}", recipientEmail, attempt)
            Return
        Catch ex As Exception When attempt < maxAttempts
            logger.LogWarning(ex, "Send attempt {Attempt} failed. Retrying...", attempt)
            Await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)))
        End Try
    Next
End Function
$vbLabelText   $csharpLabel

指数退避——第一次失败后 2 秒,第二次失败后 4 秒——可防止对过载的 SMTP 服务器进行猛烈攻击。 在生产应用程序中,用消息队列(Azure 服务总线、RabbitMQ 或 AWS SQS)替换重试循环,以便故障在应用程序重启后仍然存在。

如果 HTML 内容无法渲染,IronPDF 还会抛出 PdfException 错误。 将其与 SMTP 异常分开捕获,以便错误消息更具体:

PdfDocument pdf;
try
{
    pdf = renderer.RenderHtmlAsPdf(htmlContent);
}
catch (IronPdf.Exceptions.PdfException ex)
{
    logger.LogError(ex, "PDF rendering failed");
    throw;
}
PdfDocument pdf;
try
{
    pdf = renderer.RenderHtmlAsPdf(htmlContent);
}
catch (IronPdf.Exceptions.PdfException ex)
{
    logger.LogError(ex, "PDF rendering failed");
    throw;
}
Imports IronPdf.Exceptions

Dim pdf As PdfDocument
Try
    pdf = renderer.RenderHtmlAsPdf(htmlContent)
Catch ex As PdfException
    logger.LogError(ex, "PDF rendering failed")
    Throw
End Try
$vbLabelText   $csharpLabel

将渲染错误与交付错误分开,可以加快调试速度。 为了更全面地了解自动化文档管道中的错误处理, 5 步 PDF 生成指南详细介绍了验证模式。

如何将附件大小控制在提供商限制范围内?

大多数商业电子邮件服务商都对附件大小有限制。Gmail 将单个附件的大小限制为 25 MB; Microsoft 365 标准邮箱的默认大小为 20 MB。 样式繁多的 HTML 报表,尤其是嵌入图片的报表,可能会意外地超出这些限制。

三种方法有助于控制在限度之内:

渲染前请压缩图像。内嵌图像应使用压缩的 JPEG 或 WebP 格式,而非未压缩的 PNG 格式。 一个 600 dpi 的 PNG 格式 logo 会使 PDF 文件增加几兆字节; 通常情况下,质量为 85% 的 JPEG 图片大小小于 200 KB,但视觉效果却相同。

使用 IronPDF 的压缩设置。 PdfDocument.CompressImages 方法会在渲染后降低嵌入式位图的分辨率。 在阅读 BinaryData 之前请先调用它:

pdf.CompressImages(60); // quality 0-100
byte[] compressedPdfBytes = pdf.BinaryData;
pdf.CompressImages(60); // quality 0-100
byte[] compressedPdfBytes = pdf.BinaryData;
pdf.CompressImages(60) ' quality 0-100
Dim compressedPdfBytes As Byte() = pdf.BinaryData
$vbLabelText   $csharpLabel

将大型报告拆分成多封邮件发送。如果报告即使经过压缩后仍然超过服务商的限制,则为每个部分生成一个 PDF 文件,并分别通过单独的邮件发送。 PDF 分割和合并操作指南页面展示了如何使用 CopyPages 按页面范围分割 PdfDocument

SMTP 大小限制的外部参考: Gmail 附件限制Microsoft 365 邮件大小限制

下一步计划是什么?

现在您有了一个可以使用 IronPDF 在内存中生成 PDF 并使用 MailKit 或 System.Net.Mail 将其作为电子邮件附件发送的工作模板。 内存方法消除了磁盘读写操作,简化了容器化部署,并且可以扩展到高吞吐量场景,而无需临时文件清理。

为了深化融合:

常见问题解答

如何用 C# 将生成的 PDF 作为电子邮件附件发送?

使用IronPDF,您可以通过.NET的电子邮件发送功能,将生成的PDF文件作为电子邮件附件发送。

在 .NET 应用程序中通过电子邮件发送 PDF 文件有什么好处?

在 .NET 应用程序中通过电子邮件发送 PDF 文件有助于自动交付文档、简化业务工作流程并加强客户沟通。

IronPDF 能否处理 PDF 中用于电子邮件附件的动态内容?

是的,IronPDF动态生成PDF内容,使其适合于需要将定制PDF作为电子邮件附件发送的事件驱动应用程序。

IronPDF 电子邮件发送方法中常用的参数有哪些?

常见参数包括电子邮件主题、发件人信息和 EventArgs(确保在事件驱动应用程序中进行高效处理)。

为什么 IronPDF 适合自动交付文档?

IronPDF适合用于自动化文档递送,因为它提供可靠的PDF创建,并与C#的电子邮件发送功能集成。

是否可以使用 IronPDF 安排发送 PDF 电子邮件?

是的,IronPDF 可以集成到计划任务中,在指定时间自动发送 PDF 电子邮件,提高工作流程效率。

IronPDF 是否支持从各种数据源创建 PDF 文件作为电子邮件附件?

IronPDF支持从多个数据源创建PDF,允许开发者生成用于电子邮件附件的详细文档。

IronPDF 如何加强与客户的电子邮件沟通?

通过允许生成和发送详细的 PDF 文档作为附件,IronPDF 提高了与客户进行电子邮件交流的专业性和清晰度。

IronPDF可以用来发送发票和报告作为PDF附件吗?

是的,IronPDF非常适合生成和发送发票、报告及其他文档作为PDF附件,以满足各种业务需求。

IronPDF 在改进业务工作流程方面发挥什么作用?

IronPDF通过启用PDF文档的创建和分发来改进业务工作流程,减少手动干预和错误。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我