Een gegenereerde PDF mailen als mailerbijlage
In een oogopslag
Sectie met titel “In een oogopslag”Een factuur, bon of rapport mailen is een van de meest voorkomende dingen die je doet met een gegenereerd PDF. De schone manier om het te doen is het document bouwen, de ruwe bytes ervan nemen, en die bytes rechtstreeks aan de bijlage-API van je mailer overhandigen. Je hebt op geen enkel moment een tijdelijk bestand op schijf nodig.
Dit is een how-to. Hij gaat ervan uit dat je al weet hoe je mail verstuurt in je
framework. De NextPDF-kant is één aanroep: Document::getPdfData() retourneert de ruwe
Portable Document Format (PDF)-bytes als een string. De bijlagekant behoort volledig
aan je mailer toe — deze handleiding gebruikt Laravels Attachment::fromData() en de
Email::attach() van Symfony Mailer.
NextPDF levert geen mailhelper mee. Er is geen “mail deze PDF”-methode op een document, en je zou wantrouwend moeten zijn tegen elk voorbeeld dat er een laat zien. De bijlage-API is altijd die van je framework.
Deze pagina is de uitgaande tegenhanger van bestanden insluiten in een PDF. Die handleiding voegt bestanden in de PDF toe als ingesloten streams; deze handleiding voegt het voltooide PDF aan een e-mail toe. Het zijn verschillende operaties — verwar de twee niet.
De ruwe PDF-bytes ophalen
Sectie met titel “De ruwe PDF-bytes ophalen”Wat je verder ook doet, de NextPDF-stap is hetzelfde: produceer de bytes.
<?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() bouwt het document en retourneert de bytes ervan als een string. Hij
schrijft niets naar schijf en stuurt geen Hypertext Transfer Protocol (HTTP)-headers,
wat precies is wat je wilt voor een bijlage.
Als je het document alleen via het type NextPDF\Contracts\PdfDocumentInterface
vasthoudt (bijvoorbeeld een waarde die de framework-integratie je gaf), gebruik dan in
plaats daarvan het equivalent op contractniveau:
use NextPDF\Contracts\OutputDestination;
$bytes = $document->output(dest: OutputDestination::String);output(dest: OutputDestination::String) is gedeclareerd op PdfDocumentInterface en
retourneert dezelfde ruwe bytes zonder enige headers uit te stoten. Gebruik
getPdfData() wanneer je een concrete NextPDF\Core\Document vasthoudt, en de
output(...)-vorm wanneer je alleen de interface hebt.
Als je de Laravel- of Symfony-integratie gebruikt, herleid dan een vers document uit de container in plaats van er rechtstreeks een te construeren — zie Een gegenereerd PDF retourneren vanuit een controller voor het herleidingspad in elk framework. Alles hieronder werkt hetzelfde, ongeacht hoe je het document hebt verkregen.
Laravel: in-memory bytes toevoegen aan een Mailable
Sectie met titel “Laravel: in-memory bytes toevoegen aan een Mailable”Laravels Attachment::fromData() neemt een callback die de ruwe bytes retourneert,
plus een bestandsnaam. Er is geen tijdelijk bestand. Implementeer attachments() op je
Mailable en retourneer één Attachment.
<?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(); }}Verstuur het zoals gewoonlijk:
use App\Mail\InvoiceMail;use Illuminate\Support\Facades\Mail;
Mail::to($invoice->customerEmail)->send(new InvoiceMail($invoice));De fromData()-callback wordt lui aangeroepen wanneer het bericht wordt gebouwd, dus de
PDF wordt gegenereerd op verzendtijd, niet bij constructie. Stel
->withMime('application/pdf') in zodat de client van de ontvanger het deel als een
PDF behandelt in plaats van te gokken op basis van de extensie. Binnen een controller
kun je in plaats daarvan $message->attachData($bytes, $name, ['mime' => 'application/pdf'])
aanroepen op een ruw bericht, maar Attachment::fromData() op de Mailable is de
idiomatische moderne vorm.
Symfony Mailer: bytes toevoegen aan een Email
Sectie met titel “Symfony Mailer: bytes toevoegen aan een Email”De Email::attach() van Symfony Mailer accepteert de body als een in-memory-string,
met een expliciete bestandsnaam en content type. Wederom geen tijdelijk bestand.
<?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) neemt de bytes rechtstreeks.
Geef 'application/pdf' als derde argument door zodat het deel correct getypeerd is.
Als je liever vanuit een stream toevoegt, bestaat attachFromPath(), maar voor
gegenereerde inhoud vermijdt de in-memory-attach()-vorm een nodeloze omweg naar
schijf.
Doe het vanuit een queued job
Sectie met titel “Doe het vanuit een queued job”Een veelpagina’s-PDF bouwen en mail versturen zijn beide traag genoeg dat je ze niet op de request-thread zou moeten doen. Zet het werk in de queue. Het patroon is een job dispatchen (of, in Laravel, de Mailable zelf in de queue zetten) en de PDF op de worker bouwen.
De eenvoudigste Laravel-vorm: maak de Mailable ShouldQueue. Omdat de
Attachment::fromData()-callback draait wanneer het queued bericht wordt gebouwd, wordt
de PDF op de worker gegenereerd, niet bij dispatch.
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));Voor Symfony dispatch je een Messenger-message die de identifiers draagt (niet de
bytes), en laat je de handler de PDF bouwen en de e-mail op de worker versturen. Geef
een factuur-id door, zoek het record op in de handler, en roep de hierboven getoonde
InvoiceMailer aan. De mailer van Symfony is al asynchroon wanneer een
Messenger-transport is geconfigureerd voor SendEmailMessage, dus zelfs een
synchroon-aangeroepen send() kan naar een worker worden getransporteerd.
Als je de PDF in een toegewijde generatiejob genereert en hem daarna mailt, zie
Een PDF genereren in een queued job
voor het GeneratePdfJob / GeneratePdfMessage-oppervlak van de integratie en de
worker-veiligheidsregels ervan. Een veelvoorkomende vorm is: één job genereert en
bewaart de PDF, een tweede job (of de success-callback) leest hem terug en mailt hem.
Wanneer je de bytes binnen één enkele job in het geheugen houdt, sla je het bestand
volledig over.
Notities over grootte, inline en bijlagen
Sectie met titel “Notities over grootte, inline en bijlagen”- Bijlage, niet inline. Een gegenereerde factuur of rapport is vrijwel altijd een
apart downloadbaar bestand, dus voeg het als bijlage toe. Reserveer inline-inhoud
(
Content-Disposition: inlinemet eencid:-referentie) voor afbeeldingen die je in de HTML-body insluit — een PDF is geen body-inhoud. - Let op de grootte. E-mailbijlagen worden onderweg base64-gecodeerd, wat de payload met ongeveer een derde opblaast. Veel ontvangende servers begrenzen een bericht op ongeveer 10–25 MB na codering. Voeg voor een groot rapport een korte notificatie-e-mail met een ondertekende downloadlink toe in plaats van het bestand zelf, en serveer de PDF over HTTP — zie Een gegenereerd PDF retourneren vanuit een controller.
- Bouw één keer, voeg één keer toe. Vang de geretourneerde bytes één keer op in een variabele en hergebruik die string voor het bericht. Roep de uiteindelijke uitvoermethode niet herhaaldelijk aan voor één e-mail.
- Geheugen op de worker. De volledige PDF in het geheugen houden is prima voor
typische facturen en bonnen. Voor zeer grote documenten op een beperkte worker bewaar
je naar een tijdelijk pad met
save(), voeg je toe via Laravelattach($path)/ SymfonyEmail::attachFromPath($path), en verwijder je het bestand daarna — een schijf-omweg ingeruild voor een lagere geheugenpiek. (Voor het in-memory-pad dat overal elders op deze pagina wordt gebruikt is de byte-gebaseerde vorm LaravelAttachment::fromData()/ SymfonyEmail::attach($bytes, 'name.pdf', 'application/pdf').)
Beveiligingsnotities
Sectie met titel “Beveiligingsnotities”- Interpoleer nooit ongevalideerde gebruikersinvoer in de bijlagebestandsnaam. Geef een waarde door die je beheert (een factuurnummer dat je hebt gegenereerd), zodat de client van de ontvanger niet door een geprepareerde naam kan worden gestuurd.
- Stuur elke klant alleen zijn eigen document. Bouw de PDF uit de records van het geauthenticeerde subject binnen de job, niet uit een id die op goed vertrouwen uit de request is genomen.
- Log in een queued pad de uitzonderingsklasse en een correlatie-id bij falen, nooit het
uitzonderingsbericht of een stack trace. Schrijf nooit een leeg
catch-blok rond het bouwen-en-versturen.
Zie ook
Sectie met titel “Zie ook”- Bestanden insluiten en PDF-portfolio’s maken — het omgekeerde: bestanden in de PDF toevoegen.
- Een PDF genereren in een queued job — verplaats generatie van de request-thread.
- Een gegenereerd PDF retourneren vanuit een controller — serveer de PDF over HTTP in plaats van hem als bijlage toe te voegen.
- Laravel productiegebruik — een document uit de container herleiden en worker-veiligheid.
- Symfony productiegebruik — Messenger-workers en de document-factory.