Mengembalikan PDF yang dihasilkan dari controller
Sekilas pandang
Bagian berjudul “Sekilas pandang”Hasilkan berkas Portable Document Format (PDF) dalam aksi controller dan kembalikan sebagai respons Hypertext Transfer Protocol (HTTP). Setiap integrasi framework menyertakan helper PdfResponse yang membuat objek respons, menetapkan Content-Type: application/pdf, melampirkan header keamanan, dan membersihkan nama berkas. Panduan ini mencakup tiga mode pengiriman: pratinjau inline, unduhan berkas, dan pengiriman streaming untuk Laravel, Symfony, dan CodeIgniter 4.
Periksa prasyarat berikut lebih dulu agar jalur controller siap sebelum Anda mulai:
- Core NextPDF sudah terpasang.
- Satu integrasi framework sudah terpasang, dan service provider, bundle, atau service-nya telah terdeteksi. Verifikasi deteksinya di halaman instalasi framework Anda sebelum memulai.
- Mode streaming tidak memerlukan paket tambahan. Setiap integrasi menyertakan varian streaming bersama varian buffered.
Ini panduan praktis. Panduan ini mengasumsikan Anda sudah tahu cara merutekan request ke controller di framework Anda. Untuk contoh awal yang dapat dijalankan di tiap framework, baca quickstart framework yang ditautkan di bagian Lihat juga.
Instalasi
Bagian berjudul “Instalasi”Pasang integrasi untuk framework Anda. Jalankan salah satu perintah berikut.
composer require nextpdf/laravelcomposer require nextpdf/symfonycomposer require nextpdf/codeigniterUntuk Laravel, publikasikan konfigurasi setelah instalasi.
php artisan vendor:publish --tag=nextpdf-configSymfony mendaftarkan bundle melalui Flex, dan CodeIgniter mendeteksi service secara otomatis. Konfirmasikan deteksinya di halaman instalasi framework Anda sebelum melanjutkan.
Gambaran konseptual
Bagian berjudul “Gambaran konseptual”Setiap integrasi framework mengikuti pola tiga bagian yang sama: Anda mendapatkan dokumen baru, menulis konten ke dokumen itu, lalu meneruskannya ke factory PdfResponse yang mengembalikan respons HTTP. API dokumen (addPage(), cell(), setFont()) merupakan bagian dari permukaan engine inti, dan sama persis di semua framework. Perbedaan factory respons hanya terletak pada kelas respons yang dikembalikan, karena setiap framework memiliki tipe respons HTTP sendiri.
PdfResponse menyediakan tiga mode pengiriman. Inline menetapkan header Content-Disposition: inline, sehingga browser merender PDF di tab penampil. Download menetapkan Content-Disposition: attachment, sehingga browser menyimpan berkas. Streamed mengeluarkan body PDF dalam potongan berukuran tetap alih-alih menyangga seluruh dokumen di memori. Pilih mode ini untuk dokumen besar ketika memori puncak lebih penting daripada Content-Length yang diketahui.
Dapatkan dokumen melalui mekanisme resolusi biasa di framework Anda:
- Laravel — resolve
NextPDF\Contracts\DocumentFactoryInterfacedari container denganapp(...)dan panggilcreate(), yang mengembalikanNextPDF\Core\Documentbaru — tipe konkret yang diterima factoryPdfResponse. - Symfony — injeksikan
NextPDF\Symfony\Service\PdfFactorydan panggilcreate(), yang mengembalikanNextPDF\Core\Documentbaru dengan default dokumen yang sudah dikonfigurasi diterapkan. - CodeIgniter 4 — resolve library
PdfmelaluiServices::pdf()(atau helperpdf()), atau peroleh dokumen polos melaluipdf_document().
Permukaan API
Bagian berjudul “Permukaan API”| Kebutuhan | Laravel | Symfony | CodeIgniter 4 |
|---|---|---|---|
| Dokumen baru | app(DocumentFactoryInterface::class)->create() | PdfFactory::create() | pdf_document() / Services::pdf()->document() |
| Respons inline | PdfResponse::inline($doc, $name) | PdfResponse::inline($doc, $name) | $pdf->inline($name) / PdfResponse::inline($doc, $name) |
| Respons download | PdfResponse::download($doc, $name) | PdfResponse::download($doc, $name) | $pdf->download($name) / PdfResponse::download($doc, $name) |
| Inline streaming | PdfResponse::streamInline($doc, $name) | PdfResponse::streamInline($doc, $name) | PdfResponse::streamInline($doc, $name) |
| Download streaming | PdfResponse::streamDownload($doc, $name) | PdfResponse::streamDownload($doc, $name) | PdfResponse::streamDownload($doc, $name) |
| Tipe yang dikembalikan | Illuminate\Http\Response (streaming: StreamedResponse) | Symfony\Component\HttpFoundation\Response (streaming: StreamedResponse) | CodeIgniter\HTTP\DownloadResponse |
PdfResponse Laravel berada di NextPDF\Laravel\Http\PdfResponse, milik Symfony di NextPDF\Symfony\Http\PdfResponse, dan milik CodeIgniter di NextPDF\CodeIgniter\Http\PdfResponse. Halaman keamanan dan operasi setiap integrasi mendokumentasikan perilaku respons lengkap untuk paket tersebut: kumpulan header, aturan disposisi, dan pembersihan nama berkas. Halaman tersebut ditautkan di bagian Lihat juga.
Contoh kode — Quick start
Bagian berjudul “Contoh kode — Quick start”Berikut aksi download minimal di tiap framework. Pemanggilan dokumen menggunakan permukaan inti yang sama. Hanya kerangka controller-nya yang berubah.
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;use NextPDF\Contracts\DocumentFactoryInterface;use NextPDF\Laravel\Http\PdfResponse;
final class ReportController extends Controller{ public function download(): Response { $document = app(DocumentFactoryInterface::class)->create(); $document->addPage(); $document->cell(0, 10, 'Monthly report', newLine: true);
return PdfResponse::download($document, 'report.pdf'); }}<?php
declare(strict_types=1);
namespace App\Controller;
use NextPDF\Symfony\Http\PdfResponse;use NextPDF\Symfony\Service\PdfFactory;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Routing\Attribute\Route;
final class ReportController{ #[Route('/report', name: 'report_pdf')] public function download(PdfFactory $pdf): Response { $document = $pdf->create(); $document->addPage(); $document->cell(0, 10, 'Monthly report', newLine: true);
return PdfResponse::download($document, 'report.pdf'); }}<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\DownloadResponse;use NextPDF\CodeIgniter\Config\Services;
final class ReportController extends BaseController{ public function download(): DownloadResponse { $pdf = Services::pdf(); $pdf->document()->addPage(); $pdf->document()->cell(0, 10, 'Monthly report');
return $pdf->download('report.pdf'); }}Untuk menampilkan pratinjau di browser alih-alih mengunduhnya, ganti pemanggilan download(...) dengan inline(...) di Laravel dan Symfony, atau $pdf->inline('report.pdf') di CodeIgniter. Disposisi berubah menjadi inline, dan semua header lainnya tetap sama.
Contoh kode — Produksi
Bagian berjudul “Contoh kode — Produksi”Aksi produksi menginjeksikan dependensinya, menangkap exception paling spesifik yang didokumentasikan integrasi, mencatat kelas kegagalan tanpa membocorkan trace, dan mengembalikan error HTTP yang terdefinisi. Contoh di bawah ini menggunakan injeksi melalui constructor Laravel. Versi setara untuk Symfony dan CodeIgniter mengikuti bentuk yang sama dan tersedia di halaman penggunaan produksi tiap integrasi.
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;use NextPDF\Contracts\DocumentFactoryInterface;use NextPDF\Laravel\Http\PdfResponse;use Psr\Log\LoggerInterface;use Throwable;
final class InvoiceController extends Controller{ public function __construct( private readonly DocumentFactoryInterface $documents, private readonly LoggerInterface $logger, ) {}
public function show(int $invoiceId): Response { try { $document = $this->documents->create(); $document->addPage(); $document->cell(0, 10, "Invoice #{$invoiceId}", newLine: true);
return PdfResponse::download( $document, "invoice-{$invoiceId}.pdf", ); } catch (Throwable $exception) { // Log the exception class, never the message or a stack trace, // so internal detail does not leak into the log sink. $this->logger->error('Invoice PDF generation failed', [ 'invoice_id' => $invoiceId, 'exception' => $exception::class, ]);
return new Response('Could not generate the invoice PDF.', 500); } }}Injeksikan DocumentFactoryInterface dan panggil create() di setiap aksi. Ini mengembalikan NextPDF\Core\Document baru — tipe konkret yang diterima factory PdfResponse Laravel. Dengan me-resolve dokumen baru per request, factory tetap dapat ditukar saat pengujian. Jangan menggunakan ulang satu instance controller untuk dua dokumen yang tidak terkait dalam satu proses worker yang berjalan lama.
Untuk dokumen yang sangat besar, ganti factory buffered dengan yang streaming untuk membatasi memori puncak. Varian streaming mengembalikan StreamedResponse (Laravel dan Symfony) dan mengeluarkan body dalam potongan berukuran tetap. Varian ini sengaja tidak menyertakan Content-Length, sehingga bilah kemajuan unduhan dan proxy yang sensitif terhadap panjang tidak dapat melihat ukuran yang diketahui. Lebih baik gunakan download() / inline() yang buffered untuk respons kecil yang sensitif terhadap latensi.
$document = $this->documents->create();// ... emit content onto $document ...return PdfResponse::streamDownload($document, 'annual-report.pdf');Kasus tepi & jebakan
Bagian berjudul “Kasus tepi & jebakan”- Dokumen baru per pemanggilan. Di ketiga integrasi, dokumen adalah hasil dari factory dan baru pada setiap resolusi. Jangan men-cache dokumen yang sudah di-resolve untuk dokumen logis yang berbeda, atau lintas request dalam worker yang berjalan lama. State konten yang usang akan ikut terbawa.
- Nama berkas kosong. Nama berkas kosong yang diteruskan ke factory
PdfResponseakan beralih ke nama default (document.pdf) alih-alih menghasilkan disposisi kosong. Berikan nama berkas yang eksplisit dan bermakna. - Nama berkas non-ASCII. Respons Laravel menambahkan parameter RFC 5987
filename*=secara otomatis untuk nama non-ASCII, dan nama ASCII menggunakan parameter biasa. Jangan meng-encode nama berkas sendiri secara manual. - Respons streaming di belakang proxy yang menyangga. Proxy yang menyangga seluruh body membatalkan manfaat memori dari streaming. Konfigurasikan proxy untuk men-stream respons PDF, atau gunakan respons buffered pada jalur tersebut.
- Callback streaming Symfony. Varian streaming Symfony mengembalikan
StreamedResponsedengan callback yang mengirimkan output. Jangan menulis ke body respons sendiri setelah mengembalikannya.
Performa
Bagian berjudul “Performa”Pembuatan sinkron dalam controller memblokir request selama seluruh proses pembuatan PDF. Dokumen satu halaman biasanya masih jauh di bawah anggaran request yang umum. Untuk keluaran multi-halaman atau batch, pindahkan pembuatan dari thread request menggunakan job antrean — lihat Menghasilkan PDF dalam job antrean. Varian streaming mengurangi memori puncak untuk dokumen besar dengan konsekuensi Content-Length yang tidak diketahui. Pilih varian tersebut ketika memori menjadi kendala dan bilah kemajuan tidak diperlukan.
Catatan keamanan
Bagian berjudul “Catatan keamanan”- Factory
PdfResponsemenerapkan kumpulan header keamanan respons yang tetap dan membersihkan nama berkas unduhan di setiap integrasi. Jangan menambahkan header tersebut sendiri. - Jangan pernah menyisipkan input pengguna yang belum divalidasi langsung ke nama berkas yang Anda teruskan ke factory. Berikan nilai yang Anda kendalikan, dan biarkan factory membersihkannya sebagai lapisan kedua.
- Dalam blok catch, catat kelas exception dan identifier korelasi, bukan pesan atau trace exception. Trace mentah dalam log sink merupakan kebocoran informasi.
- Jangan pernah menulis blok
catchyang kosong. Setiap contoh di sini mencatat dan mengembalikan respons error yang terdefinisi.
Halaman keamanan dan operasi setiap integrasi mendokumentasikan model ancaman integrasi tersebut: kumpulan header, aturan pembersihan nama berkas, dan masa berlaku pengikatan dokumen.
Kesesuaian
Bagian berjudul “Kesesuaian”Panduan ini tidak membuat klaim standar normatif apa pun. Setiap pemanggilan API yang ditampilkan berasal dari permukaan publik terverifikasi pada integrasi yang disebutkan, dan sudah diperiksa silang dengan halaman quickstart serta penggunaan produksi tiap paket. Halaman penggunaan produksi upstream yang ditautkan di bagian Lihat juga mendokumentasikan semantik header dan perilaku pengikatan container yang menjadi sandaran integrasi, beserta sitasi PSR-nya. Halaman cookbook ini menyatakan kembali penggunaannya dan mengacu pada halaman-halaman tersebut untuk sitasi normatif.
Lihat juga
Bagian berjudul “Lihat juga”- Menghasilkan PDF dalam job antrean — pindahkan pekerjaan ini dari thread request.
- Penggunaan produksi Laravel — controller yang terhubung melalui DI, kumpulan header, dan kontrak pengikatan PSR-11.
- Quickstart Symfony — controller, inline, streaming, dan model respons.
- Quickstart CodeIgniter —
Services::pdf(), helperpdf(), danPdfResponse. - Memilih integrasi — pilih paket framework yang tepat.