İçeriğe geç
getnextpdf.com

Oluşturulmuş bir PDF'i posta eki olarak e-posta ile gönderme

Bir faturayı, makbuzu ya da raporu e-posta ile göndermek, oluşturulmuş bir PDF ile yaptığınız en yaygın şeylerden biridir. Bunu yapmanın temiz yolu, belgeyi oluşturmak, ham baytlarını almak ve o baytları doğrudan posta yöneticinizin ek API’sine vermektir. Hiçbir noktada diskte geçici bir dosyaya ihtiyacınız yoktur.

Bu bir nasıl yapılır kılavuzudur. Çerçevenizde posta göndermeyi zaten bildiğiniz varsayılır. NextPDF tarafı tek bir çağrıdır: Document::getPdfData(), ham Taşınabilir Belge Biçimi (PDF) baytlarını bir dize olarak döndürür. Ek tarafı tamamen posta yöneticinize aittir — bu kılavuz, Laravel’in Attachment::fromData() ve Symfony Mailer’ın Email::attach() yöntemlerini kullanır.

NextPDF bir posta yardımcısı göndermez. Bir belgede “bu PDF’i e-postala” yöntemi yoktur ve böyle bir tane gösteren her örnekten kuşkulanmalısınız. Ek API’si her zaman çerçevenizindir.

Bu sayfa, dosyaları bir PDF’in içine gömmenin giden karşılığıdır. O kılavuz, dosyaları gömülü akışlar olarak PDF’in içine ekler; bu kılavuz, bitmiş PDF’i bir e-postaya ekler. Bunlar farklı işlemlerdir — ikisini karıştırmayın.

Başka ne yaparsanız yapın, NextPDF adımı aynıdır: baytları üretin.

<?php
declare(strict_types=1);
use NextPDF\Core\Document;
// Standalone entrypoint: the static factory wires the default dependencies.
// (The bare `new Document(...)` constructor requires injected collaborators.)
$document = Document::createStandalone();
$document->addPage();
$document->cell(0, 10, 'Invoice #1042', newLine: true);
// Raw PDF bytes, built in memory. No file is written.
$bytes = $document->getPdfData();

getPdfData(), belgeyi oluşturur ve baytlarını bir dize olarak döndürür. Diske hiçbir şey yazmaz ve hiçbir Köprü Metni Aktarım Protokolü (HTTP) başlığı göndermez; ki bir ek için tam olarak istediğiniz budur.

Belgeyi yalnızca NextPDF\Contracts\PdfDocumentInterface türü üzerinden tutuyorsanız (örneğin çerçeve tümleştirmesinin size verdiği bir değer), bunun yerine sözleşme düzeyindeki eşdeğeri kullanın:

use NextPDF\Contracts\OutputDestination;
$bytes = $document->output(dest: OutputDestination::String);

output(dest: OutputDestination::String), PdfDocumentInterface üzerinde bildirilir ve herhangi bir başlık yaymadan aynı ham baytları döndürür. Somut bir NextPDF\Core\Document tuttuğunuzda getPdfData()’yı, yalnızca arabirime sahip olduğunuzda output(...) biçimini kullanın.

Laravel ya da Symfony tümleştirmesini kullanıyorsanız, bir belgeyi doğrudan oluşturmak yerine kapsayıcıdan taze bir belge çözümleyin — her çerçevedeki çözümleme yolu için bkz. Bir denetleyiciden oluşturulmuş bir PDF döndürme. Aşağıdaki her şey, belgeyi nasıl edindiğinizden bağımsız olarak aynı şekilde çalışır.

Laravel: bellek içi baytları bir Mailable’a ekleme

“Laravel: bellek içi baytları bir Mailable’a ekleme” başlıklı bölüm

Laravel’in Attachment::fromData() yöntemi, ham baytları döndüren bir geri çağırma ile bir dosya adı alır. Geçici dosya yoktur. Mailable’ınızda attachments()’ı uygulayın ve tek bir Attachment döndürün.

app/Mail/InvoiceMail.php
<?php
declare(strict_types=1);
namespace App\Mail;
use App\Models\Invoice;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use NextPDF\Core\Document;
final class InvoiceMail extends Mailable
{
public function __construct(private readonly Invoice $invoice) {}
public function envelope(): Envelope
{
return new Envelope(subject: "Invoice #{$this->invoice->number}");
}
public function content(): Content
{
return new Content(markdown: 'mail.invoice');
}
/** @return array<int, Attachment> */
public function attachments(): array
{
return [
Attachment::fromData(fn (): string => $this->buildPdf(), "invoice-{$this->invoice->number}.pdf")
->withMime('application/pdf'),
];
}
private function buildPdf(): string
{
// Standalone document: the static factory wires the default
// dependencies, so this example is self-contained. If you use the
// nextpdf/laravel integration, resolve a document via its documented
// binding instead — see the integration page linked below.
$document = Document::createStandalone();
$document->addPage();
$document->cell(0, 10, "Invoice #{$this->invoice->number}", newLine: true);
return $document->getPdfData();
}
}

Onu her zamanki gibi gönderin:

use App\Mail\InvoiceMail;
use Illuminate\Support\Facades\Mail;
Mail::to($invoice->customerEmail)->send(new InvoiceMail($invoice));

fromData() geri çağırması, ileti oluşturulurken tembel biçimde çağrılır; bu nedenle PDF, kuruluşta değil, gönderim zamanında oluşturulur. ->withMime('application/pdf') ayarlayın; böylece alıcının istemcisi, parçayı uzantıdan tahmin etmek yerine bir PDF olarak ele alır. Bir denetleyici içinde, bunun yerine ham bir iletide $message->attachData($bytes, $name, ['mime' => 'application/pdf']) çağırabilirsiniz; ama Mailable’da Attachment::fromData(), deyimsel modern biçimdir.

Symfony Mailer’ın Email::attach() yöntemi, gövdeyi açık bir dosya adı ve içerik türüyle birlikte bellek içi bir dize olarak kabul eder. Yine geçici dosya yoktur.

src/Mailer/InvoiceMailer.php
<?php
declare(strict_types=1);
namespace App\Mailer;
use NextPDF\Core\DocumentFactory;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
final class InvoiceMailer
{
public function __construct(
private readonly MailerInterface $mailer,
private readonly DocumentFactory $documents,
) {}
public function sendInvoice(string $to, int $invoiceId): void
{
// Build a fresh document from the factory, which wires the default
// dependencies for you. (Use Document::createStandalone() if you do
// not have the factory injected.)
$document = $this->documents->create();
$document->addPage();
$document->cell(0, 10, "Invoice #{$invoiceId}", newLine: true);
$email = (new Email())
->from('billing@example.com')
->to($to)
->subject("Invoice #{$invoiceId}")
->text('Your invoice is attached.')
->attach(
$document->getPdfData(),
"invoice-{$invoiceId}.pdf",
'application/pdf',
);
$this->mailer->send($email);
}
}

attach(string $body, ?string $name, ?string $contentType), baytları doğrudan alır. Parçanın doğru türlenmesi için üçüncü bağımsız değişken olarak 'application/pdf' geçirin. Bir akıştan eklemeyi tercih ederseniz attachFromPath() vardır; ama oluşturulmuş içerik için bellek içi attach() biçimi, diske gereksiz bir gidiş-dönüşten kaçınır.

Çok sayfalı bir PDF oluşturmak ve posta göndermek; ikisi de, bunları istek iş parçacığında yapmamanız gerekecek kadar yavaştır. İşi kuyruğa alın. Örüntü, bir iş göndermek (ya da Laravel’de, Mailable’ın kendisini kuyruğa almak) ve PDF’i işçide oluşturmaktır.

En basit Laravel biçimi: Mailable’ı ShouldQueue yapın. Attachment::fromData() geri çağırması, kuyruğa alınmış ileti oluşturulurken çalıştığı için, PDF gönderimde değil, işçide oluşturulur.

app/Mail/InvoiceMail.php (queued)
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
final class InvoiceMail extends Mailable implements ShouldQueue
{
// ... same envelope(), content(), attachments() as above ...
}
// Dispatched to the queue; the worker builds the PDF and sends the mail.
Mail::to($invoice->customerEmail)->queue(new InvoiceMail($invoice));

Symfony için, baytları değil (kimlikleri) taşıyan bir Messenger iletisi gönderin ve işleyicinin PDF’i oluşturup e-postayı işçide göndermesine izin verin. Bir fatura kimliği geçirin, kaydı işleyicide arayın ve yukarıda gösterilen InvoiceMailer’ı çağırın. SendEmailMessage için bir Messenger taşıması yapılandırıldığında Symfony’nin posta yöneticisi zaten eşzamansızdır; bu nedenle eşzamanlı olarak çağrılan bir send() bile bir işçiye taşınabilir.

PDF’i ayrı bir oluşturma işinde üretip sonra e-postalarsanız, tümleştirmenin GeneratePdfJob / GeneratePdfMessage yüzeyi ve işçi güvenliği kuralları için bkz. Kuyruğa alınmış bir işte PDF oluşturma. Yaygın bir biçim şudur: bir iş PDF’i üretir ve kaydeder, ikinci bir iş (ya da başarı geri çağırması) onu geri okur ve e-postalar. Baytları tek bir iş boyunca bellekte tuttuğunuzda, dosyayı tamamen atlarsınız.

  • Satır içi değil, ek. Oluşturulmuş bir fatura ya da rapor neredeyse her zaman ayrı, indirilebilir bir dosyadır; bu yüzden onu ekleyin. Satır içi içeriği (cid: başvurusuyla Content-Disposition: inline), HTML gövdesine gömdüğünüz görüntüler için ayırın — bir PDF, gövde içeriği değildir.
  • Boyuta dikkat edin. E-posta ekleri aktarımda base64 ile kodlanır; bu da yükü kabaca üçte bir oranında şişirir. Birçok alıcı sunucu, bir iletiyi kodlamadan sonra yaklaşık 10–25 MB ile sınırlar. Büyük bir rapor için, dosyanın kendisi yerine imzalı bir indirme bağlantısı içeren kısa bir bildirim e-postası ekleyin ve PDF’i HTTP üzerinden sunun — bkz. Bir denetleyiciden oluşturulmuş bir PDF döndürme.
  • Bir kez oluştur, bir kez ekle. Döndürülen baytları bir değişkene bir kez yakalayın ve o dizeyi ileti için yeniden kullanın. Bir e-posta için son çıktı yöntemini tekrar tekrar çağırmayın.
  • İşçide bellek. Tam PDF’i bellekte tutmak, tipik faturalar ve makbuzlar için sorun değildir. Kısıtlı bir işçideki çok büyük belgeler için, save() ile bir geçici yola kaydedin, Laravel attach($path) / Symfony Email::attachFromPath($path) aracılığıyla ekleyin, sonra dosyayı silin — daha düşük bir bellek zirvesi için bir disk gidiş-dönüşü takas ederek. (Bu sayfada her yerde kullanılan bellek içi yol için, bayt tabanlı biçim Laravel Attachment::fromData() / Symfony Email::attach($bytes, 'name.pdf', 'application/pdf')’dir.)
  • Doğrulanmamış kullanıcı girdisini ek dosya adına asla aktarmayın. Denetlediğiniz bir değer (sizin ürettiğiniz bir fatura numarası) geçirin; böylece alıcının istemcisi, hazırlanmış bir adla yönlendirilemez.
  • Her müşteriye yalnızca kendi belgesini gönderin. PDF’i, istekten güvenle alınan bir kimlikten değil, kimliği doğrulanmış öznenin kayıtlarından işin içinde oluşturun.
  • Kuyruğa alınmış bir yolda, başarısızlıkta istisna sınıfını ve bir ilişkilendirme kimliğini günlüğe yazın, asla istisna iletisini ya da bir yığın izlemesini değil. Oluştur-ve-gönder etrafına asla boş bir catch bloğu yazmayın.