ข้ามไปยังเนื้อหา
getnextpdf.com

Pro รุ่น

Webview

Webview ส่งมอบ PDF ที่ผ่าน linearized (Fast Web View) ผ่าน HTTP เพื่อให้ไคลเอนต์เริ่มเรนเดอร์หน้าที่ 1 จาก prefix ส่วนต้นขนาดเล็กได้ในขณะที่ไฟล์ส่วนที่เหลือยังถูกส่งอยู่ มันห่อหุ้มไบต์ดิบเป็น LinearizedDocument ตอบคำขอ Range ด้วยการตอบกลับแบบ partial-content ตาม RFC 9110 ผ่าน ByteRangeResponder แบบ PSR-7 และสามารถพิสูจน์ได้ (ผ่าน FirstPageProber) ว่าหน้าแรกอยู่ครบในตัวเองภายใน prefix

ความสามารถนี้มาพร้อมกับ NextPDF Pro (nextpdf/pro) และเปิดใช้งานด้วย license envelope ระดับ Pro การนำไปใช้งานที่ไม่มีสิทธิ์ดังกล่าวจะไม่โหลดคลาสของความสามารถนี้ เปรียบเทียบรุ่นและรับสิทธิ์การใช้งาน

ไม่มีแฟล็กสิทธิ์การใช้งานต่อฟีเจอร์แยกต่างหาก ตัวตอบกลับถูกต่อสายเข้ากับ PSR-17 factory ของคุณเองในขณะรัน อันได้แก่ ResponseFactoryInterface และ StreamFactoryInterface และชนิดสื่อตั้งค่าเริ่มต้นเป็น application/pdf ในฐานะอาร์กิวเมนต์ของ constructor ไม่ใช่สวิตช์ของสิทธิ์การใช้งาน

Terminal window
composer require nextpdf/pro

โค้ดอยู่ภายใต้ namespace NextPDF\Pro\Webview

PDF ที่ผ่าน linearized ถูกจัดวางเพื่อให้หน้าแรกของเอกสาร อันได้แก่ พจนานุกรมพารามิเตอร์ linearization, hint stream หลัก และออบเจกต์ของหน้าที่ 1 อยู่ในส่วนต้นที่สิ้นสุดที่ออฟเซ็ต /E Webview เปลี่ยนการจัดวางนั้นให้กลายเป็นการส่งมอบแบบโพรเกรสซีฟ

LinearizedDocument::fromBytes() แจงไบต์ผ่าน LinearizationView ฝั่งอ่านของ Core (Pro ไม่เคยทำการแจง linearization ขึ้นใหม่) และปฏิเสธสิ่งใดก็ตามที่ไม่ใช่เอกสาร linearized ที่ใช้งานได้ ได้แก่ ไม่ได้ linearized เลย, ความยาว /L ที่ประกาศไว้ไม่ตรงกับความยาวไบต์จริง หรือออฟเซ็ตจบหน้าแรก /E ที่ไม่ใช่ออฟเซ็ตค่าบวกภายในไฟล์ การสร้างจึงเป็นแบบ total เมื่อคุณถือ LinearizedDocument อยู่ ทุกออฟเซ็ตที่มันเปิดเผยจึงเชื่อถือได้

จากนั้น ByteRangeResponder ตอบคำขอ HTTP มันถูกอิมพลีเมนต์เทียบกับ PSR-7 / PSR-17 เท่านั้น โดยไม่ผูกกับเฟรมเวิร์ก มันประกาศ Accept-Ranges: bytes และ ETag SHA-256 แบบ strong และกำหนดได้แน่นอนเสมอ แจงเฮดเดอร์ Range ของไคลเอนต์ตาม RFC 9110 §14 และคืนค่าอย่างใดอย่างหนึ่งระหว่าง 200 OK แบบเต็ม, ช่วงเดียวแบบ 206 Partial Content, การตอบกลับแบบ 206 multipart/byteranges สำหรับหลายช่วง หรือ 416 Range Not Satisfiable

FirstPageProber คือฝั่งพิสูจน์เชิงโครงสร้าง มันวัดปริมาณ prefix ของหน้าแรก สัดส่วนของไฟล์ทั้งไฟล์ที่ prefix นั้นแทน และว่า hint stream หลักอยู่ภายในนั้นครบถ้วนหรือไม่ ซึ่งเป็นคุณสมบัติที่ให้โปรแกรมอ่านระบุตำแหน่งออบเจกต์ของหน้าที่ 1 ได้จาก prefix เพียงอย่างเดียว

Webview ไม่เคยแจง linearization ขึ้นใหม่ด้วยตัวเอง มันยืม LinearizationView ฝั่งอ่านของ Core มาใช้ ดังนั้นเลเยอร์การส่งมอบจึงสืบทอดตัวแจงที่ผ่านการตรวจสอบเพียงตัวเดียว แทนที่จะเป็นสำเนาที่สองที่อาจคลาดเคลื่อน การสร้างเป็นแบบ total โดยเจตนา LinearizedDocument::fromBytes() ปฏิเสธการจัดวางที่ผิดรูปแบบตั้งแต่ต้น ดังนั้นทุกออฟเซ็ตที่การตอบกลับ Range เชื่อถือจึงถูกตรวจสอบก่อนแล้ว ตัวตอบกลับพูดเพียง PSR-7 และ PSR-17 เท่านั้น ดังนั้นโค้ดเดียวกันจึงให้บริการ PDF ที่ผ่าน linearized จาก HTTP stack ใดก็ได้ วินัยดังกล่าวคือสิ่งที่ทำให้การส่งมอบช่วงแบบโพรเกรสซีฟปลอดภัยพอที่จะเปิดให้ไคลเอนต์ที่ไม่น่าเชื่อถือใช้งานในปริมาณมาก

ที่มาของการออกแบบ: การสร้างเอกสารปริมาณมาก

การให้บริการแบบ byte-range โพรเกรสซีฟทำงานอย่างไร

หัวข้อที่มีชื่อว่า “การให้บริการแบบ byte-range โพรเกรสซีฟทำงานอย่างไร”
  1. สร้าง LinearizedDocument จากไบต์ PDF ที่เรนเดอร์แล้ว อินพุตที่ไม่ถูกต้องจะยก UnsupportedDocumentException ตั้งแต่ต้น
  2. ส่งเอกสารและ PSR-7 ServerRequestInterface ที่เข้ามาให้ ByteRangeResponder::respond() ตัวตอบกลับอ่าน Range (และเงื่อนไข If-Range ที่เป็นทางเลือก) แล้วสร้าง PSR-7 ResponseInterface ที่ถูกต้อง
  3. ไคลเอนต์ขอ prefix ส่วนต้นก่อน (หรือคุณ push มันด้วย firstPageResponse()) เรนเดอร์หน้าที่ 1 จากนั้นขอช่วงที่เหลือเมื่อผู้ใช้เลื่อนดู

แบบจำลอง byte-range ใช้ออฟเซ็ตแบบ inclusive ตาม RFC 9110 §14.1.2 กล่าวคือ ByteRange หนึ่งคือ firstBytelastByte เหนือการแทนเอกสารที่มี contentLength และฟิลด์ Content-Range ของมันคือ bytes first-last/length

  • LinearizedDocument::fromBytes() เป็นแบบ total เอกสารที่ไม่ได้ linearized, /L ที่ไม่ตรงกัน หรือออฟเซ็ต /E ที่ไม่เป็นค่าบวก / อยู่นอกไฟล์ แต่ละกรณีจะยก UnsupportedDocumentException แทนที่จะผลิตเอกสารที่ไม่ปลอดภัย
  • ETag เป็น strong SHA-256 entity-tag เหนือไบต์ที่แน่นอน ถูก memoise ครั้งเดียวตอนสร้าง อินพุตการเรนเดอร์ที่เหมือนกันให้ไบต์ที่เหมือนกัน จึงให้ ETag ที่เหมือนกัน ดังนั้นแคชและ If-Range จึงทำงานได้อย่างคาดเดาได้
  • คำขอที่ไม่มี Range ที่ใช้ได้จะคืนค่า 200 OK พร้อมเนื้อหาเต็ม If-Range ที่ไม่ตรงกับ strong ETag ปัจจุบันจะทำให้ Range ถูกเพิกเฉยและคืนค่า 200 แบบเต็ม (RFC 9110 §13.1.5) เฉพาะรูปแบบ strong entity-tag ของ If-Range เท่านั้นที่ได้รับการยอมรับ ส่วน If-Range แบบ HTTP-date จะถือว่าไม่ตรงกัน
  • หน่วยช่วงที่ไม่รู้จักหรือ Range ที่ไม่ถูกต้องตามไวยากรณ์จะถูกเพิกเฉยและคืนค่า 200 แบบเต็ม (RFC 9110 §14.2)
  • ช่วงเดียวที่ตอบสนองได้จะคืนค่า 206 Partial Content พร้อม Content-Range หลายช่วงที่ตอบสนองได้จะคืนค่า 206 multipart/byteranges ช่วงไบต์ที่ถูกต้องแต่ไม่มีช่วงใดตอบสนองได้จะคืนค่า 416 พร้อม Content-Range: bytes */length (RFC 9110 §15.3.7)
  • firstPageResponse() ส่ง 206 ที่บรรจุช่วงไบต์ของหน้าแรก [0, /E - 1] พอดี ซึ่งเป็นรูปแบบ server-push ของ “หน้าแรกก่อนดาวน์โหลดเต็ม”

ต่อไปนี้สะท้อน public API ที่จัดทำเอกสารไว้ repository ไม่ได้มาพร้อมตัวอย่างที่รันได้สำหรับโมดูลนี้

use NextPDF\Pro\Webview\LinearizedDocument;
use NextPDF\Pro\Webview\ByteRangeResponder;
$document = LinearizedDocument::fromBytes($pdfBytes);
$responder = new ByteRangeResponder($responseFactory, $streamFactory);
$response = $responder->respond($document, $request);
use NextPDF\Pro\Webview\LinearizedDocument;
use NextPDF\Pro\Webview\ByteRangeResponder;
use NextPDF\Pro\Webview\FirstPageProber;
use NextPDF\Pro\Webview\Exception\UnsupportedDocumentException;
try {
$document = LinearizedDocument::fromBytes($pdfBytes);
} catch (UnsupportedDocumentException $e) {
// Not a usable linearized document — fall back to plain full delivery.
// ...
return;
}
$prober = new FirstPageProber($document);
if ($prober->isFirstPageSelfContained()) {
// Push exactly the first page's bytes for an instant render.
$response = (new ByteRangeResponder($responseFactory, $streamFactory))
->firstPageResponse($document);
}
  • Webview ต้องการ PDF ที่ผ่าน linearized อย่างแท้จริง หากเอกสารที่เรนเดอร์แล้วไม่ได้ linearized ให้เปิดใช้ linearization ในเวลาเรนเดอร์ หรือให้บริการด้วยการส่งมอบเต็มแบบธรรมดา โดย respondToBytes() ยังคงให้บริการช่วงเหนือไบต์ใด ๆ (ที่ไม่ได้ linearized) ได้เมื่อคุณต้องการเพียงการรองรับช่วง ไม่ใช่ความหมายเชิงหน้าแรก
  • การอัปเดตแบบ incremental มีความสำคัญ เอกสารที่ถูกผนวกเกินกว่าค่า /L ที่ประกาศไว้จะถูกปฏิเสธเป็นความยาวที่ไม่ตรงกัน เพราะออฟเซ็ต byte-range จะไม่น่าเชื่อถืออีกต่อไป
  • ตัวตอบกลับจำกัดจำนวนช่วงที่แตกต่างกันที่มันยอมรับต่อคำขอ คำขอที่ขอช่วงที่รวมกันแล้วมากกว่าเพดาน หรือขอจำนวนไบต์รวมมากกว่าการแทนเอกสารทั้งหมด จะถูกเพิกเฉย Range และได้รับบริการเป็น 200 แบบเต็ม

prefix ของหน้าแรกคือออฟเซ็ตจบหน้าแรก /E ที่ถูกจำกัดให้ไม่เกินความยาวไฟล์ ดังนั้น FirstPageProber::prefixFraction() จึงรายงานว่าการดึงข้อมูลครั้งแรกมีขนาดเล็กเพียงใดเมื่อเทียบกับไฟล์ทั้งไฟล์ สำหรับเอกสารหลายหน้า นี่คือจุดประสงค์ทั้งหมดของ Fast Web View การสร้างการตอบกลับจะตัดสตริงไบต์ในหน่วยความจำออกมา ต้นทุนเป็นสัดส่วนกับไบต์ที่เลือก ETag ถูกคำนวณครั้งเดียวต่อเอกสาร ให้วัดผลด้วยเอกสารที่เป็นตัวแทน

ให้ถือว่าอินพุตไม่น่าเชื่อถือ LinearizedDocument::fromBytes() ตรวจสอบ invariant ของ linearization ก่อนที่จะใช้ออฟเซ็ตใด ๆ ตัวตอบกลับปฏิเสธ contentType ที่มีอักขระควบคุมเพื่อป้องกัน header injection อนุมาน multipart boundary ที่รับประกันว่าจะไม่ปรากฏภายในเนื้อหา และรวมช่วงที่ทับซ้อนกันพร้อมจำกัดจำนวนและขนาดรวมเพื่อป้องกัน denial-of-service ประเภท multipart range-amplification (Apache HTTPD CVE-2011-3192) โมดูลนี้ไม่บันทึกเนื้อหาเอกสารใด ๆ

การส่งมอบแบบ byte-range เป็นไปตาม RFC 9110 (HTTP Semantics) ได้แก่ §14 สำหรับคำขอช่วง, §13.1.5 สำหรับ If-Range และ §15.3.7 สำหรับ 416 แบบจำลองเอกสาร linearized คือการจัดวาง Fast Web View ที่อธิบายไว้ใน ISO 32000-2 Annex F โมดูลนี้ไม่ยืนยันตัวระบุข้อกำหนดภายนอกเพิ่มเติมใดนอกเหนือจากพฤติกรรมที่การทดสอบของมันตรวจสอบยืนยันแล้ว

Enterprise ไม่เปลี่ยนพฤติกรรมของ Webview Enterprise เพิ่มฟีเจอร์การปฏิบัติตามข้อกำหนดและการเก็บถาวรในระดับสูงกว่าซึ่งถูกบันทึกไว้แยกต่างหาก ฟีเจอร์เหล่านั้นไม่จำเป็นต่อการให้บริการ PDF ที่ผ่าน linearized เหนือช่วงไบต์

หน้านี้บันทึกเฉพาะพฤติกรรมที่สังเกตได้จากภายนอกและพื้นผิว public API ที่รองรับเท่านั้น เส้นทาง namespace ภายใน, คลาสตัวช่วย, ตารางกลไก, ชื่อไฟล์ runbook และคำนำหน้าของ ticket อยู่นอกขอบเขต