ส่งคืน PDF ที่สร้างจากคอนโทรลเลอร์
ภาพรวมโดยย่อ
หัวข้อที่มีชื่อว่า “ภาพรวมโดยย่อ”สร้างไฟล์ Portable Document Format (PDF) ภายในแอ็กชันของคอนโทรลเลอร์ แล้วส่งคืนในรูปแบบการตอบสนอง Hypertext Transfer Protocol (HTTP) การรวมของแต่ละเฟรมเวิร์กมีตัวช่วย PdfResponse สำหรับสร้างอ็อบเจกต์การตอบสนอง กำหนด Content-Type: application/pdf แนบเฮดเดอร์ความปลอดภัย และล้างชื่อไฟล์ให้ปลอดภัย คู่มือนี้ครอบคลุมโหมดการส่งมอบสามแบบ ได้แก่ การแสดงตัวอย่างแบบอินไลน์ การดาวน์โหลดไฟล์ และการส่งมอบแบบสตรีม สำหรับ Laravel, Symfony และ CodeIgniter 4
ตรวจสอบข้อกำหนดเบื้องต้นเหล่านี้ เพื่อให้เส้นทางของคอนโทรลเลอร์พร้อมใช้งานก่อนเริ่มต้น:
- ติดตั้ง core ของ NextPDF แล้ว
- ติดตั้งการรวมเฟรมเวิร์กหนึ่งรายการแล้ว และระบบค้นพบ service provider, bundle หรือ service ของการรวมนั้นแล้ว ตรวจสอบการค้นพบในหน้าการติดตั้งสำหรับเฟรมเวิร์กของคุณก่อนเริ่มต้น
- โหมดสตรีมไม่ต้องใช้แพ็กเกจเพิ่มเติม การรวมทั้งหมดมีตัวแปรแบบสตรีมควบคู่กับตัวแปรแบบบัฟเฟอร์
หน้านี้เป็นคู่มือวิธีทำ โดยถือว่าคุณทราบวิธีกำหนดเส้นทางคำขอไปยังคอนโทรลเลอร์ในเฟรมเวิร์กของคุณอยู่แล้ว สำหรับตัวอย่างแรกที่รันได้ในแต่ละเฟรมเวิร์ก โปรดอ่าน quickstart ของเฟรมเวิร์กที่ลิงก์ไว้ในส่วนดูเพิ่มเติม
การติดตั้ง
หัวข้อที่มีชื่อว่า “การติดตั้ง”ติดตั้งการรวมสำหรับเฟรมเวิร์กของคุณด้วยคำสั่งใดคำสั่งหนึ่งต่อไปนี้
composer require nextpdf/laravelcomposer require nextpdf/symfonycomposer require nextpdf/codeigniterสำหรับ Laravel ให้เผยแพร่การกำหนดค่าหลังติดตั้ง
php artisan vendor:publish --tag=nextpdf-configSymfony จะลงทะเบียน bundle ผ่าน Flex ส่วน CodeIgniter จะค้นพบ service โดยอัตโนมัติ ยืนยันการค้นพบในหน้าการติดตั้งของเฟรมเวิร์กของคุณก่อนดำเนินการต่อ
ภาพรวมเชิงแนวคิด
หัวข้อที่มีชื่อว่า “ภาพรวมเชิงแนวคิด”การรวมของทุกเฟรมเวิร์กใช้รูปแบบสามขั้นตอนเดียวกัน: สร้างเอกสารใหม่ เขียนเนื้อหาลงในเอกสารนั้น แล้วส่งต่อไปยังแฟกทอรี PdfResponse ที่ส่งคืนการตอบสนอง HTTP API ของเอกสาร (addPage(), cell(), setFont()) คือพื้นผิวของเอนจิน core และเหมือนกันในทุกเฟรมเวิร์ก แฟกทอรีการตอบสนองต่างกันเฉพาะคลาสการตอบสนองที่ส่งคืน เพราะแต่ละเฟรมเวิร์กมีชนิดการตอบสนอง HTTP ของตนเอง
PdfResponse มีโหมดการส่งมอบสามแบบ ได้แก่ อินไลน์ ซึ่งกำหนดเฮดเดอร์ Content-Disposition: inline เพื่อให้เบราว์เซอร์เรนเดอร์ PDF ในแท็บตัวแสดงเอกสาร; ดาวน์โหลด ซึ่งกำหนด Content-Disposition: attachment เพื่อให้เบราว์เซอร์บันทึกไฟล์; และ สตรีม ซึ่งปล่อยเนื้อหา PDF เป็นชิ้นที่มีขนาดคงที่ แทนการบัฟเฟอร์เอกสารทั้งฉบับไว้ในหน่วยความจำ เลือกโหมดนี้สำหรับเอกสารขนาดใหญ่เมื่อหน่วยความจำสูงสุดสำคัญกว่าการทราบ Content-Length ที่แน่นอน
รับเอกสารผ่านกลไกการรีโซลฟ์ตามปกติของเฟรมเวิร์กของคุณ:
- Laravel — รีโซลฟ์
NextPDF\Contracts\DocumentFactoryInterfaceจากคอนเทนเนอร์ด้วยapp(...)แล้วเรียกcreate()ซึ่งส่งคืนNextPDF\Core\Documentใหม่ — ชนิดที่เป็นรูปธรรมซึ่งแฟกทอรีPdfResponseรับ - Symfony — ฉีด
NextPDF\Symfony\Service\PdfFactoryแล้วเรียกcreate()ซึ่งส่งคืนNextPDF\Core\Documentใหม่พร้อมค่าเริ่มต้นของเอกสารที่กำหนดค่าไว้แล้ว - CodeIgniter 4 — รีโซลฟ์ไลบรารี
Pdfผ่านServices::pdf()(หรือตัวช่วยpdf()) หรือรับเอกสารเปล่าผ่านpdf_document()ได้
พื้นผิว API
หัวข้อที่มีชื่อว่า “พื้นผิว API”| ประเด็น | Laravel | Symfony | CodeIgniter 4 |
|---|---|---|---|
| เอกสารใหม่ | app(DocumentFactoryInterface::class)->create() | PdfFactory::create() | pdf_document() / Services::pdf()->document() |
| การตอบสนองแบบอินไลน์ | PdfResponse::inline($doc, $name) | PdfResponse::inline($doc, $name) | $pdf->inline($name) / PdfResponse::inline($doc, $name) |
| การตอบสนองแบบดาวน์โหลด | PdfResponse::download($doc, $name) | PdfResponse::download($doc, $name) | $pdf->download($name) / PdfResponse::download($doc, $name) |
| อินไลน์แบบสตรีม | PdfResponse::streamInline($doc, $name) | PdfResponse::streamInline($doc, $name) | PdfResponse::streamInline($doc, $name) |
| ดาวน์โหลดแบบสตรีม | PdfResponse::streamDownload($doc, $name) | PdfResponse::streamDownload($doc, $name) | PdfResponse::streamDownload($doc, $name) |
| ชนิดที่ส่งคืน | Illuminate\Http\Response (สตรีม: StreamedResponse) | Symfony\Component\HttpFoundation\Response (สตรีม: StreamedResponse) | CodeIgniter\HTTP\DownloadResponse |
คลาส PdfResponse ของ Laravel อยู่ที่ NextPDF\Laravel\Http\PdfResponse ของ Symfony อยู่ที่ NextPDF\Symfony\Http\PdfResponse และของ CodeIgniter อยู่ที่ NextPDF\CodeIgniter\Http\PdfResponse หน้าความปลอดภัยและการปฏิบัติการของการรวมแต่ละรายการบันทึกพฤติกรรมการตอบสนองทั้งหมดของแพ็กเกจนั้น ได้แก่ ชุดเฮดเดอร์ กฎ disposition และการล้างชื่อไฟล์ให้ปลอดภัย ลิงก์ของหน้าเหล่านั้นอยู่ในส่วนดูเพิ่มเติม
ตัวอย่างโค้ด — Quick start
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — Quick start”ตัวอย่างต่อไปนี้คือแอ็กชันการดาวน์โหลดขั้นต่ำในแต่ละเฟรมเวิร์ก การเรียกใช้งานเอกสารใช้พื้นผิว core เดียวกัน เปลี่ยนเฉพาะโครงร่างของคอนโทรลเลอร์เท่านั้น
<?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'); }}หากต้องการแสดงตัวอย่างในเบราว์เซอร์แทนการดาวน์โหลด ให้สลับการเรียก download(...) เป็น inline(...) ใน Laravel และ Symfony หรือ $pdf->inline('report.pdf') ใน CodeIgniter disposition จะเปลี่ยนเป็น inline และเฮดเดอร์อื่นทั้งหมดจะเหมือนเดิม
ตัวอย่างโค้ด — สำหรับการใช้งานจริง
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — สำหรับการใช้งานจริง”แอ็กชันสำหรับการใช้งานจริงควรรับการพึ่งพาผ่าน injection จับ exception ที่เฉพาะเจาะจงที่สุดตามที่การรวมระบุไว้ในเอกสาร บันทึกคลาสของ exception โดยไม่เปิดเผย trace และส่งคืนข้อผิดพลาด HTTP ที่กำหนดไว้ ตัวอย่างด้านล่างใช้ constructor injection ของ Laravel รูปแบบที่เทียบเท่าสำหรับ Symfony และ CodeIgniter ใช้แนวทางเดียวกันและอยู่ในหน้าการใช้งานจริงของการรวมแต่ละรายการ
<?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); } }}ฉีด DocumentFactoryInterface และเรียก create() ในแต่ละแอ็กชัน การเรียกนี้ส่งคืน NextPDF\Core\Document ใหม่ — ชนิดที่เป็นรูปธรรมซึ่งแฟกทอรี PdfResponse ของ Laravel รับ การรีโซลฟ์เอกสารใหม่ต่อหนึ่งคำขอทำให้สามารถสลับแฟกทอรีในการทดสอบได้ อย่านำอินสแตนซ์คอนโทรลเลอร์เดียวกันมาใช้ซ้ำกับเอกสารสองฉบับที่ไม่เกี่ยวข้องกันภายในกระบวนการ worker ที่ทำงานต่อเนื่องเป็นเวลานานหนึ่งกระบวนการ
สำหรับเอกสารขนาดใหญ่มาก ให้แทนที่แฟกทอรีแบบบัฟเฟอร์ด้วยแบบสตรีมเพื่อจำกัดหน่วยความจำสูงสุด ตัวแปรแบบสตรีมส่งคืน StreamedResponse (Laravel และ Symfony) และปล่อยเนื้อหาเป็นชิ้นที่มีขนาดคงที่ ตัวแปรนี้จงใจละเว้น Content-Length ดังนั้นแถบความคืบหน้าการดาวน์โหลดและพร็อกซีที่อาศัยความยาวจะไม่เห็นขนาดที่ทราบ ควรใช้ download() / inline() แบบบัฟเฟอร์สำหรับการตอบสนองขนาดเล็กที่ไวต่อเวลาแฝง
$document = $this->documents->create();// ... emit content onto $document ...return PdfResponse::streamDownload($document, 'annual-report.pdf');กรณีขอบและข้อควรระวัง
หัวข้อที่มีชื่อว่า “กรณีขอบและข้อควรระวัง”- เอกสารใหม่ต่อการเรียกหนึ่งครั้ง ในการรวมทั้งสามรายการ เอกสารถูกสร้างจากแฟกทอรี ซึ่งสร้างใหม่ในการรีโซลฟ์แต่ละครั้ง อย่าแคชเอกสารที่รีโซลฟ์แล้วเพื่อใช้ข้ามเอกสารเชิงตรรกะ หรือข้ามคำขอใน worker ที่ทำงานต่อเนื่องเป็นเวลานาน สถานะเนื้อหาเก่าจะตกค้างต่อไป
- ชื่อไฟล์ว่าง ชื่อไฟล์ว่างที่ส่งไปยังแฟกทอรี
PdfResponseจะย้อนกลับไปใช้ชื่อเริ่มต้น (document.pdf) แทนที่จะสร้าง disposition ว่างเปล่า ส่งชื่อไฟล์ที่ชัดเจนและมีความหมาย - ชื่อไฟล์ที่ไม่ใช่ ASCII การตอบสนองของ Laravel จะเพิ่มพารามิเตอร์ RFC 5987
filename*=โดยอัตโนมัติสำหรับชื่อที่ไม่ใช่ ASCII ส่วนชื่อที่เป็น ASCII จะใช้พารามิเตอร์ธรรมดา อย่าเข้ารหัสชื่อไฟล์ด้วยตนเอง - การตอบสนองแบบสตรีมที่อยู่หลังพร็อกซีที่บัฟเฟอร์ พร็อกซีที่บัฟเฟอร์เนื้อหาทั้งหมดจะลบล้างประโยชน์ด้านหน่วยความจำของการสตรีม กำหนดค่าพร็อกซีให้สตรีมการตอบสนอง PDF หรือใช้การตอบสนองแบบบัฟเฟอร์บนเส้นทางนั้น
- คอลแบ็กแบบสตรีมของ Symfony ตัวแปรแบบสตรีมของ Symfony ส่งคืน
StreamedResponseโดยคอลแบ็กของตัวแปรนี้จะ flush เอาต์พุต อย่าเขียนลงในเนื้อหาการตอบสนองด้วยตนเองหลังจากส่งคืนแล้ว
ประสิทธิภาพ
หัวข้อที่มีชื่อว่า “ประสิทธิภาพ”การสร้างแบบซิงโครนัสภายในคอนโทรลเลอร์จะบล็อกคำขอตลอดการสร้าง PDF ทั้งหมด เอกสารหน้าเดียวมักอยู่ภายในงบประมาณคำขอทั่วไปได้อย่างสบาย สำหรับเอาต์พุตหลายหน้าหรือแบบกลุ่ม ให้ย้ายการสร้างออกจากเธรดของคำขอด้วยงานที่จัดคิว — ดู สร้าง PDF ในงานที่จัดคิว ตัวแปรแบบสตรีมลดหน่วยความจำสูงสุดสำหรับเอกสารขนาดใหญ่ โดยแลกกับการที่ไม่ทราบ Content-Length เลือกใช้ตัวแปรเหล่านี้เมื่อหน่วยความจำเป็นข้อจำกัดและไม่ต้องการแถบความคืบหน้า
หมายเหตุด้านความปลอดภัย
หัวข้อที่มีชื่อว่า “หมายเหตุด้านความปลอดภัย”- แฟกทอรี
PdfResponseใช้ชุดเฮดเดอร์ที่เพิ่มความแข็งแกร่งให้การตอบสนองซึ่งกำหนดไว้ตายตัว และล้างชื่อไฟล์สำหรับดาวน์โหลดให้ปลอดภัยในการรวมทั้งหมด อย่าเพิ่มเฮดเดอร์เหล่านั้นด้วยตนเอง - อย่าแทรกอินพุตของผู้ใช้ที่ไม่ผ่านการตรวจสอบลงในชื่อไฟล์ที่คุณส่งไปยังแฟกทอรีโดยตรงเด็ดขาด ส่งค่าที่คุณควบคุมได้ และให้แฟกทอรีล้างค่านั้นให้ปลอดภัยเป็นชั้นที่สอง
- ในบล็อก catch ให้บันทึกคลาสของ exception และตัวระบุสำหรับเชื่อมโยง ไม่ใช่ข้อความหรือ trace ของ exception trace ดิบในแหล่งบันทึกถือเป็นการรั่วไหลของข้อมูล
- อย่าเขียนบล็อก
catchที่ว่างเปล่าเด็ดขาด ตัวอย่างทุกตัวอย่างที่นี่จะบันทึกและส่งคืนการตอบสนองข้อผิดพลาดที่กำหนดไว้
หน้าความปลอดภัยและการปฏิบัติการของการรวมแต่ละรายการบันทึกแบบจำลองภัยคุกคามของการรวมนั้น ได้แก่ ชุดเฮดเดอร์ กฎการล้างชื่อไฟล์ให้ปลอดภัย และอายุของการผูกเอกสาร
ความสอดคล้อง
หัวข้อที่มีชื่อว่า “ความสอดคล้อง”คู่มือนี้ไม่ได้กล่าวอ้างเชิงบรรทัดฐานต่อมาตรฐานใด การเรียก API ทุกตัวที่แสดงคือพื้นผิวสาธารณะที่ตรวจสอบแล้วของการรวมที่ระบุไว้ ซึ่งตรวจสอบเทียบกับหน้า quickstart และหน้าการใช้งานจริงของแต่ละแพ็กเกจ หน้าการใช้งานจริงต้นทางที่ลิงก์ไว้ในส่วนดูเพิ่มเติมบันทึกความหมายของเฮดเดอร์และพฤติกรรมการผูกคอนเทนเนอร์ที่การรวมพึ่งพา พร้อมด้วยการอ้างอิง PSR หน้า cookbook นี้ทวนการใช้งานและส่งต่อการอ้างอิงเชิงบรรทัดฐานไปยังหน้าเหล่านั้น
ดูเพิ่มเติม
หัวข้อที่มีชื่อว่า “ดูเพิ่มเติม”- สร้าง PDF ในงานที่จัดคิว — ย้ายงานนี้ออกจากเธรดของคำขอ
- การใช้งานจริงของ Laravel — คอนโทรลเลอร์ที่เชื่อมต่อด้วย DI ชุดเฮดเดอร์ และสัญญาการผูก PSR-11
- Symfony quickstart — คอนโทรลเลอร์ อินไลน์ สตรีม และแบบจำลองการตอบสนอง
- CodeIgniter quickstart —
Services::pdf()ตัวช่วยpdf()และPdfResponseที่เกี่ยวข้อง - เลือกการรวม — เลือกแพ็กเกจเฟรมเวิร์กที่เหมาะสม