Kirim PDF yang dihasilkan via email sebagai lampiran mailer
Sekilas pandang
Bagian berjudul “Sekilas pandang”Mengirim faktur, kuitansi, atau laporan via email adalah salah satu hal paling umum yang Anda lakukan dengan PDF yang dihasilkan. Cara bersih melakukannya adalah dengan membangun dokumen, mengambil byte mentahnya, dan menyerahkan byte itu langsung ke API lampiran mailer Anda. Anda tidak memerlukan file sementara di disk pada titik mana pun.
Ini adalah sebuah how-to. Ia mengasumsikan Anda sudah tahu cara mengirim mail di
framework Anda. Sisi NextPDF-nya adalah satu pemanggilan:
Document::getPdfData() mengembalikan byte Portable Document Format (PDF) mentah
sebagai string. Sisi lampiran sepenuhnya menjadi milik mailer Anda — panduan ini
menggunakan Attachment::fromData() Laravel dan Email::attach() Symfony Mailer.
NextPDF tidak mengirim helper mail. Tidak ada metode “email this PDF” pada sebuah dokumen, dan Anda sebaiknya curiga terhadap contoh apa pun yang menunjukkan satu. API lampiran selalu milik framework Anda.
Halaman ini adalah pasangan keluar dari menanamkan file di dalam PDF. Panduan itu melampirkan file ke dalam PDF sebagai stream yang ditanamkan; panduan ini melampirkan PDF jadi ke sebuah email. Keduanya operasi berbeda — jangan mengacaukan keduanya.
Ambil byte PDF mentah
Bagian berjudul “Ambil byte PDF mentah”Apa pun lagi yang Anda lakukan, langkah NextPDF-nya sama: hasilkan byte-nya.
<?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() membangun dokumen dan mengembalikan byte-nya sebagai string. Ia
tidak menulis apa pun ke disk dan tidak mengirim header Hypertext Transfer
Protocol (HTTP) apa pun, yang persis seperti yang Anda inginkan untuk sebuah
lampiran.
Jika Anda memegang dokumen hanya melalui tipe
NextPDF\Contracts\PdfDocumentInterface (misalnya, sebuah nilai yang diserahkan
integrasi framework kepada Anda), gunakan padanan tingkat-kontraknya sebagai
gantinya:
use NextPDF\Contracts\OutputDestination;
$bytes = $document->output(dest: OutputDestination::String);output(dest: OutputDestination::String) dideklarasikan pada
PdfDocumentInterface dan mengembalikan byte mentah yang sama tanpa memancarkan
header apa pun. Gunakan getPdfData() ketika Anda memegang NextPDF\Core\Document
konkret, dan bentuk output(...) ketika Anda hanya memiliki interface-nya.
Jika Anda menggunakan integrasi Laravel atau Symfony, resolusi sebuah dokumen baru dari container alih-alih membangunnya secara langsung — lihat Kembalikan PDF yang dihasilkan dari sebuah controller untuk jalur resolusi di setiap framework. Segala hal di bawah bekerja sama terlepas dari bagaimana Anda memperoleh dokumennya.
Laravel: lampirkan byte in-memory ke sebuah Mailable
Bagian berjudul “Laravel: lampirkan byte in-memory ke sebuah Mailable”Attachment::fromData() Laravel menerima sebuah callback yang mengembalikan byte
mentah, ditambah sebuah nama file. Tidak ada file sementara. Implementasikan
attachments() pada Mailable Anda dan kembalikan satu 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(); }}Kirim seperti biasa:
use App\Mail\InvoiceMail;use Illuminate\Support\Facades\Mail;
Mail::to($invoice->customerEmail)->send(new InvoiceMail($invoice));Callback fromData() dipanggil secara lazy saat pesannya dibangun, sehingga PDF
dihasilkan pada waktu kirim, bukan saat konstruksi. Atur
->withMime('application/pdf') agar client penerima memperlakukan bagian itu
sebagai PDF alih-alih menebak dari ekstensi. Di dalam sebuah controller Anda dapat
sebagai gantinya memanggil $message->attachData($bytes, $name, ['mime' => 'application/pdf'])
pada sebuah pesan mentah, tetapi Attachment::fromData() pada Mailable adalah
bentuk modern yang idiomatik.
Symfony Mailer: lampirkan byte ke sebuah Email
Bagian berjudul “Symfony Mailer: lampirkan byte ke sebuah Email”Email::attach() Symfony Mailer menerima body sebagai string in-memory, dengan
nama file dan content type yang eksplisit. Lagi-lagi, tanpa file sementara.
<?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) menerima byte-nya
secara langsung. Berikan 'application/pdf' sebagai argumen ketiga agar bagiannya
bertipe dengan benar. Jika Anda lebih suka melampirkan dari sebuah stream,
attachFromPath() ada, tetapi untuk konten yang dihasilkan bentuk in-memory
attach() menghindari putaran ke disk yang tidak perlu.
Lakukan dari sebuah job yang di-queue
Bagian berjudul “Lakukan dari sebuah job yang di-queue”Membangun PDF multi-halaman dan mengirim mail keduanya cukup lambat sehingga Anda sebaiknya tidak melakukannya di thread permintaan. Queue pekerjaannya. Polanya adalah mendispatch sebuah job (atau, di Laravel, men-queue Mailable itu sendiri) dan membangun PDF di worker.
Bentuk Laravel paling sederhana: jadikan Mailable ShouldQueue. Karena callback
Attachment::fromData() berjalan saat pesan yang di-queue dibangun, PDF dihasilkan
di worker, bukan saat 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));Untuk Symfony, dispatch sebuah pesan Messenger yang membawa pengidentifikasi
(bukan byte-nya), dan biarkan handler membangun PDF dan mengirim email di worker.
Berikan sebuah id faktur, cari record-nya di handler, dan panggil InvoiceMailer
yang ditunjukkan di atas. Mailer Symfony sudah asinkron ketika sebuah transport
Messenger dikonfigurasi untuk SendEmailMessage, sehingga bahkan send() yang
dipanggil secara sinkron dapat ditransportasikan ke sebuah worker.
Jika Anda menghasilkan PDF dalam sebuah job generasi khusus lalu mengirimnya via
email, lihat
Hasilkan PDF dalam sebuah job yang di-queue
untuk permukaan GeneratePdfJob / GeneratePdfMessage milik integrasi dan aturan
keamanan-worker-nya. Bentuk yang umum adalah: satu job menghasilkan dan menyimpan
PDF, sebuah job kedua (atau callback sukses) membacanya kembali dan mengirimnya
via email. Ketika Anda menjaga byte-nya di memori sepanjang satu job, Anda
melewatkan file-nya sepenuhnya.
Catatan ukuran, inline, dan lampiran
Bagian berjudul “Catatan ukuran, inline, dan lampiran”- Lampiran, bukan inline. Sebuah faktur atau laporan yang dihasilkan hampir
selalu merupakan file terpisah yang dapat diunduh, jadi lampirkan. Cadangkan
konten inline (
Content-Disposition: inlinedengan referensicid:) untuk gambar yang Anda tanamkan di body HTML — sebuah PDF bukan konten body. - Perhatikan ukurannya. Lampiran email di-encode-base64 saat transit, yang membengkakkan payload kira-kira sepertiga. Banyak server penerima membatasi sebuah pesan pada sekitar 10–25 MB setelah encoding. Untuk laporan besar, lampirkan email notifikasi singkat dengan tautan unduhan bertanda tangan alih-alih file itu sendiri, dan layani PDF melalui HTTP — lihat Kembalikan PDF yang dihasilkan dari sebuah controller.
- Bangun sekali, lampirkan sekali. Tangkap byte yang dikembalikan sekali ke dalam sebuah variabel dan gunakan ulang string itu untuk pesannya. Jangan memanggil metode keluaran final berulang kali untuk satu email.
- Memori di worker. Memegang seluruh PDF di memori baik-baik saja untuk faktur
dan kuitansi tipikal. Untuk dokumen yang sangat besar pada worker yang terbatas,
simpan ke path sementara dengan
save(), lampirkan via Laravelattach($path)/ SymfonyEmail::attachFromPath($path), lalu hapus file-nya — menukar putaran ke disk dengan puncak memori yang lebih rendah. (Untuk jalur in-memory yang digunakan di mana pun lain pada halaman ini, bentuk berbasis-byte adalah LaravelAttachment::fromData()/ SymfonyEmail::attach($bytes, 'name.pdf', 'application/pdf').)
Catatan keamanan
Bagian berjudul “Catatan keamanan”- Jangan pernah menginterpolasi input pengguna yang tidak tervalidasi ke nama file lampiran. Berikan nilai yang Anda kendalikan (sebuah nomor faktur yang Anda hasilkan), agar client penerima tidak dapat dikemudikan oleh nama yang dibuat dengan sengaja.
- Kirim setiap pelanggan hanya dokumennya sendiri. Bangun PDF dari record subjek yang terautentikasi di dalam job, bukan dari sebuah id yang diterima atas kepercayaan dari permintaan.
- Pada jalur yang di-queue, catat kelas exception dan sebuah id korelasi saat
gagal, jangan pernah pesan exception atau stack trace. Jangan pernah menulis
blok
catchkosong di sekitar build-and-send.
Lihat juga
Bagian berjudul “Lihat juga”- Tanamkan file dan buat portofolio PDF — kebalikannya: lampirkan file ke dalam PDF.
- Hasilkan PDF dalam sebuah job yang di-queue — pindahkan generasi keluar dari thread permintaan.
- Kembalikan PDF yang dihasilkan dari sebuah controller — layani PDF melalui HTTP alih-alih melampirkannya.
- Penggunaan produksi Laravel — meresolusi dokumen dari container dan keamanan worker.
- Penggunaan produksi Symfony — worker Messenger dan factory dokumen.